没记错的话,比赛那天正好是圣诞节,就只看了这一道pwn题,我还没做出来。我太菜了。
有一说一,ida换成7.5版本之后,一些去掉符号表的函数也能被识别出来了,ida更好用了呢。
题目程序分为两块,先看第一块,登陆。
先输入一个username,再输入一个passcode,程序会判断passcode和在16行随机生成的数是否相等,如果相等就会通过判断。(16行,后面的函数是来读取随机数的)
这里的读取用的是自写函数,sub_13BB,跟进去看看。
箭头所指是会在我们输入的username后面加"\x00"的,用来截断字符串,但是这里明显可以溢出。
当输入64个字符时,会在65处加上"\x00",执行snprintf的时候,这个位置又会被随机数覆盖,在18行进行输出的时候,由于没有截断,会把生成的随机数也输出。这样就得到passcode了。这样第一部分就搞定了。接下来看第二部分。
这里题目说了,程序用的是蒙特卡罗算法求圆周率的。到底是怎么求的呢?
个人感觉和抛硬币一样,只要你抛得次数足够多,最后的统计出的抛出正面和反面的概率就越接近1/2。这就是这个算法的核心,多次随机实验来求概率,通过概率再进行推算。跑远了...
题目是让最后得出的圆周率的误差小于0.0000001就可以cat flag。是让我们输入一个实验的次数,理论上,只要次数足够多,就可以直接拿flag。但是程序在第一块有alarm函数,是有计时的。所以不行,毕竟还是个pwn题嘛,找找漏洞点。
这里有一个17行有一个"%llu"其实挺引人注目的,这个是long long int的缩写,而程序上写了,v1是int类型变量,说明这里可以进行溢出。可以将v2赋值成3.1415926,同时让v1==0,这样v0==0,就可以拿到flag了。
这里有一个知识点就是,浮点数应在内存中应该怎么表示呢?我以前写过文章专门讲解过,这里我就直接通过例子看了。
1 #include<stdio.h> 2 float a; 3 int main() 4 { 5 a=3.1415926; 6 getchar(); 7 return 0; 8 }
gcc编译一下,看内存分布。
因为这只是给v2赋值的,还需要让v1==0,因为是小端序的原因,内存中应该是
00000000 DA0F4940 00000000 (前面的8个数代表v1的内存分布,后面的16个数是v2的内存分布)
所以这里需要输入的数值应该是4632251120704552960,既能让v1==0,又能让v2==3.1415926。这样就能拿到flag了。
exp:
1 from pwn import * 2 3 p = process('./pi') 4 context.log_level = 'debug' 5 6 p.sendlineafter('Username: ','a'*0x3f+'b') 7 p.recvuntil('b') 8 password = p.recvuntil('.')[:-1] 9 print 'password-->'+password 10 p.sendlineafter('Passcode: ',password) 11 p.recvuntil('N = ') 12 p.sendline('4632251120704552960') 13 p.recv() 14 p.close()
蒙特卡洛算法图片来源:https://crazism.net/9454.html