passcode
main
先file看一下,32位。
用IDA打开,f5
可以看到,在调用完 welcome() 后立即调用了 login()。也就是说,welcome()栈帧的栈底 和 login()栈帧的栈底 是一样的。
welcome()
通过 __isoc99_scanf() 读取name字符串,字符串大小为100。且由
这一行可知,在 welcome() 的栈帧中,name字符串的起始位置在 ebp-70h 的地方。
login()
这里分别通过
来获得 passcode1 和 passcode2 的值。要注意的是,这里 scanf(“%d”) 中没有出现取地址符,也就是说,scanf(“%d”) 会直接把栈上的数据当作指针,并将读入的数据存放到这个“指针”指的“地址”上。
以第一个红框为例,它对应着 passcode1 的读入。scanf的参数有两个,第一个是格式化字符串,第二个是地址表列。按照参数从右到左入栈。所以下面这段话,将 ebp-0x10 处的数据 放到了 esp+0x4 处 作为了 scanf的地址表项参数
scanf的第一个参数即 格式化字符串 由下面这两句话传入
0x8048783 处的数据如下:
所以对于第一个 scanf(), 它的第二个参数的数值为在login()栈帧的 ebp-0x10 处的数据。
对于passcode2 的分析同理。
Check
Analysis
从login()的逻辑来看,只要 v1 = 0x528E6 和 0xCC07C9 程序就能执行到 return system()。而且 welcome()和 login() 的 ebp 是相同的,有没有可能通过构造 welcome的输入 来控制 v1 v2 的值呢?
从前边的ida分析来看,在login()的栈帧中,passcode1位置在 ebp-10h, 在welcome()栈帧中,name字符串起始位置在 ebp-70h。由于welcome()和login()调用连续,他们的栈帧的ebp其实是一样的。如下图,两个栈帧对比如下:
70h - 10h = 60h = 96 < 100。也就是说,我们在welcome()中输入的name的第 97 - 100 的字符,在 login()栈帧中恰好是作为第一个 scanf 的第二个参数(即地址表项)。而在login()中,调用第一次 scanf 时传入的 %d 将会写到这个地址中。这样我们能控制 passcode1。
用peda的checksec检查发现开启了canary保护
由于passcode1已经是在长度为100的name的最后四个字节,因此不可能通过继续增加name的输入来控制 passcode2,否则会触发canary。
科普:GOT
linux中,ELF编译系统采用了一种叫延迟绑定(lazy binding)的技术。若ELF文件调用了定义在共享库中的函数,那ELF文件中就存在 GOT(全局偏移表) 和 PLT(过程链接表),其中GOT存放在 .data 段(已初始化的全局C变量),而PLT存放在 .text 段(已编译程序的机器代码)。对于一般函数,PLT表和GOT表一一对应。
当第一次调用共享库中的函数时,该函数对应的GOT表项中存放的是对应PLT表中的push1条目的地址。程序调用时,执行函数对应PLT的第一条指令时会先通过对应GOT跳转到PLT表中的下一条指令,之后通过一系列操作,将对应GOT覆盖为函数的真实地址,并执行该函数。等到下一次调用该函数时,程序一样先执行函数对应PLT的第一条指令,之后通过GOT表会直接执行该函数,因为GOT表中已经是函数的真实地址了。
Thinking
通过之前的分析,我们能通过控制name的最后四个字节,结合passcode1的scanf来实现对任意地址的写入。加上 GOT表是在 .data 段,是可写的。因此一个想法就是:我们可以将 printf 的GOT表 覆写为 system函数的地址。由前可知,当再次调用 printf 时,会通过 printf的GOT表执行 system函数。
printf 的 GOT地址可以通过 pwntools 工具获得。也可以通过 objdump 获得,如下.
对于system函数地址,由于程序中已经提供了,所以这里直接取 80485e3
这里对应着login()中的
梳理一下思路:按照程序的流程,welcome(),我们输入100个字符,其中最后四个是 printf的GOT地址。之后程序进入login,调用scanf(”%d”)时,以 %d 形式将我们输入的数据( system(“/bin/cat flag”))读入,并写入到 printf的GOT地址。接下去,程序会执行 printf(“enter passcode2 : “); 即再次调用printf函数,但实际执行的是 system函数。
要注意的是,由于是以 %d 形式读入,所以输入时应为 134514147 (0x80485e3 = 134514147)
Exp
|
|
flag
local test
|
|
pwnable.kr
|
|
FLAG: Sorry mom.. I got confused about scanf usage :(