pwn刷题
ctfshow
pwn70 不可见字符
orw + 不可见字符绕过strlen()
1 | orw = asm(''' |
不可打印字符(non-printable characters)是指在计算机字符集中不对应于可见符号或图形的字符。这些字符通常用于控制文本的格式、流控制、数据结构或者是特殊的功能,而不是直接表示人类可读的内容。
1. 不可打印字符的分类
不可打印字符通常可以分为以下几类:
控制字符:这些字符用于控制设备(如打印机或终端)的行为。例如:
- 换行(Line Feed, LF):
\n(ASCII 10),用于换行。 - 回车(Carriage Return, CR):
\r(ASCII 13),用于返回到行首。 - 制表符(Tab):
\t(ASCII 9),用于插入水平制表。 - 警告音(Bell):
\a(ASCII 7),用于发出声音提示。
- 换行(Line Feed, LF):
终止字符:如
null字符\0(ASCII 0),常用于标记字符串的结束。特殊控制字符:如
ESC(ASCII 27),用于引入控制序列。
2. 不可打印字符的ASCII码
以下是一些常见的不可打印字符及其对应的 ASCII 码:
| 字符 | 描述 | ASCII 码 |
|---|---|---|
NUL |
空字符(null) | 0 |
SOH |
标题开始 | 1 |
STX |
正文开始 | 2 |
ETX |
正文结束 | 3 |
EOT |
传输结束 | 4 |
ENQ |
请求 | 5 |
ACK |
确认 | 6 |
BEL |
响铃 | 7 |
BS |
退格 | 8 |
HT |
水平制表符 | 9 |
LF |
换行 | 10 |
VT |
垂直制表符 | 11 |
FF |
换页 | 12 |
CR |
回车 | 13 |
SO |
转换为旁路 | 14 |
SI |
转换为内部 | 15 |
ESC |
转义 | 27 |
DEL |
删除 | 127 |
3. 应用场景
不可打印字符在计算机编程和文本处理中的应用非常广泛,包括但不限于:
- 文本格式和控制:在终端或打印机输出时,控制字符用来格式化输出。
- 数据结构:在某些数据结构中,特定的字符可能被用作分隔符或结束符。
- 协议设计:在网络通信协议中,不可打印字符可能用于标记消息的开始和结束,或者表示特定的控制信息。
不可打印字符在安全中的作用
在安全场景中,不可打印字符经常被用作绕过过滤器和检查的手段。例如:
- 缓冲区溢出攻击:攻击者可能会插入不可打印字符,以欺骗安全检查并执行恶意代码。
- 注入攻击:在某些情况下,攻击者可能会通过注入不可打印字符来操纵输入,绕过输入验证。
pwn162
有些东西在早些版本是有缺陷的?
- 远程环境:Ubuntu 16.04
思路:
首先明确在free时,程序会在free的chunk的fd处写0,并且show()函数无用所以要打stdout
- 利用unsortedbin的特性在chunk上留下libc地址(通过chunk shrink),该chunk称chunk A
- 利用程序的特性,部分覆盖使其1/16指向_IO_2_1_stdout_附近
- fastbin_dup(partial write)使chunk A链入fastbin那么stdout附近的那个chunk(B)就也被链入了fastbin
- 申请出chunk B修改stdout的flag->0xfbad1800 和 write_base的低位 -> \x00,并计算地址
- 用realloc调整栈帧使one_gadget成立
为什么覆盖libc地址的低2字节为b”\xdd\x25”?
在gdb中查看_IO_list_all的地址,可以看到stdout,如下:
1 | pwndbg> p (void*)_IO_list_all |
因为我们要在stdout上写入,就必须把它申请出来,并且fastbin取出chunk是要检查size域的,所以我们必须要在stdout附近伪造chunk,一般在libc地址上伪造的chunk的size都为7f
通过观察发现如下:
1 | pwndbg> x/30gx 0x7f8fe0a6b620-0x43 |
如上,在 0x7f8fe0a6b5dd 处可以很好的伪造size,后三位为 5dd 所以我们写libc低2字节为b”\xdd\x25”就有1/16的概率指向stdout附近。
最后再次通过fastbin_dup申请到__malloc_hook附近用one_gadget来getshell
1 | #!/usr/bin/env python3 |
pwn164
这题用的是realloc分配内存。
1 | realloc(void* ptr, size_t size) |
关于realloc有几个重要的知识点:
realloc(ptr,0)相当于free函数
realloc(0.size)相当于malloc函数
realloc(ptr,size)
newsize<size:进行分割,剩下的chunk如果大于等于MINSIZE则进行free
newsize<size:
- next 为top且满足需求,直接从top切割
- next为freechunk 且满足要求先合并(unlink)再切割
- next不满足要求进行malloc(newsize),然后进行数据拷贝,free原chun
关于 realloc的详细介绍,看这位大佬的blog: realloc相关知识点
解题:
本题就两个函数一个realloc,一个free函数。很简单
在realloc的时候,因为每次都是使用realloc_ptr,并且没有变化,导致每次申请的的chunk都会写在realloc_ptr指向的地址,再次申请比上一次的size大就可以往后溢
利用思路:
主要利用基础:UAF,double free
注意到题目中是没有show类型的函数的,所以想进行地址泄露应该要靠IO_FILE攻击
难点:利用realloc进行堆块合并后,再利用UAF进行地址覆盖
polar靶场
sandbox
输入 $0 即可绕过那几个检查
1. $0 的基本含义
- 在脚本中:
$0表示正在运行的脚本名称。
例如,执行脚本./test.sh时,$0的值是./test.sh。 - 在交互式 Shell 中:
$0表示当前 Shell 的名称(如bash、sh、zsh)。
例如,在 Bash 中输入echo $0,输出通常是bash。
creeper
1 | __int64 game() |
输入15个字符就行,如:
1 | aaaabbbbaaaabb\n |
hahaha
后门直接溢出就行,高版本需要有栈平衡
1 | payload = b'a'*(0x30+8) + p64(0x0000000000400441) + p64(0x400596) |
cat
溢出改栈上的变量
1 | ps = b"lovecat" |
buu
rip
题目给出ubuntu18,.且是64位程序需要考虑栈平衡
lambda部分省略
1 | ret = 0x0000000000401016 |
warmup_csaw_2016
ubuntu16.04,题目给出了一个地址可以cat flag.txt
1 | ru(b'WOW:') |
ciscn_2019_n_1
栈溢出修改栈上的变量,计算出两个变量的位之差
1 | from pwn import * |
pwn1_sctf_2016
ubuntu16.04(在其他版本也行)程序将I替换为you通过strcpy造成了溢出,有后门函数
1 | padding = 20 # 0x3c = 60; 60/3=20; |
level0
ubuntu16.04 read()栈溢出有后门
1 |
|
[第五空间2019 决赛]PWN5
格式化字符串,任意地址写
1 | # 思路一:格式化字符串改atoi为system,第二次读入'/bin/sh\x00' |
jarvisoj_level2
read()溢出,有system和/bin/sh
1 | bin_sh = 0x0804A024 |
ciscn_2019_n_8
直接写入14个p32(17)就行了
1 | >>> from pwn import * |
bjdctf_2020_babystack
自己输入输入长度,溢出,有后门
1 | ru(b'[+]Please input the length of your name:') |
ciscn_2019_c_1
strlen()有'\0'截断,所以在加密函数中可以直接跳出循环,然后打ret2libc即可
libc版本,buu上的64位2.27
1 | puts_plt = 0x4006e0 |
收获
C语言代码伪代码要一行一行分析,分析循环跳出的条件
jarvisoj_level2_x64
read()栈溢出
1 | from pwn import * |
ciscn_2019_s_3
SROP
1 | mov_rax_0xf = 0x4004DA |
pwnable
start

主要问题:泄露esp和esp每次溢出后都会因为add esp 14h;retn发生变化
应对:由于溢出(0x14)的存在先写脚本调试运行,返回地址明显可以看出要设为(0x08048087)。第一次溢出后esp地址将通过write(1,ecx,14h)泄露出来,再次从read溢出后可计算泄露出来的esp和再次从read溢出后的esp的差值
1 | #pause() |
强网杯
flag-market
由上图可知存在格式化字符串。flag在堆上,通过栈中留下的一个地址刚好是堆地址,程序输出过flag后可由find "flag{"查到flag在堆中的偏移如下:
思路:
开启时读取flag并将stream放在栈上,这是一个堆地址,进去发现flag在堆上
输入255时会scanf读取到bss上有溢出,此时发现该位置下面便是printf参数所在地址,将printf参数覆盖成格式化字符串,泄露堆地址。
flag在堆上,直接找到位置,通过栈变量s给它放到栈上,通过%s泄露出来即可
1 | sla(b'2.exit\n', b'1') |