Pwnable.kr 提示之 dragon 篇

2018-12-13  本文已影响0人  JackoQm

前前言

本人的个人博客网址:www.QmSharing.space,所有的文章都可以在里面找到,欢迎各位大佬前来参观并留下宝贵的建议,大家一起学习一起成长 :-)

前言

我知道网上已经有很多这个网站的 writeup 和答案, 但我觉得它们要不就直接把 writeup 拿出来讲逻辑, 就是直接把解答过程手把手教你, 私以为缺少了一点分析过程, 对于类似我这种不喜欢被剧透的玩家有点不友好. 因此, 本系列目标是对 Pwnable 系列题目进行分析并为卡壳了的玩家提供一个思路, 答案并不会直接在本文给出, 如果真的只需要看答案, 可以拉到本文末尾的答案链接, 或者出门右转去 Google, 能搜出一大堆. 本系列持续更新中...(但最近玩 Pwnable.kr 的时间有点少, 所以更新的有点慢, 请见谅)

难度分析

解决本题需要结合多个漏洞(这个后面讲), 而且注入点也比较隐蔽, 如果没有之前题目的经验的话, 可能还是比较难以完成.

基本检查

dragon: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=a7a354f09b431b4523192272c448af835b35ae9b, not stripped

可以看到是个基本的 32 位动态链接程序

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

那看来这题不存在栈溢出和栈段执行, 可能是利用一些逻辑漏洞或者 UAF 来进行重定向

分析

这题切入点的确很隐蔽, 用 IDA 逆向一下可以发现, 本题的检查还是很严格的, 基本不存在栈溢出或者劫持 got 的机会, 我一开始是注意到了这里:

printf("Welcome to Secret Level!\nInput Password : ");
__isoc99_scanf("%10s", &s1);
// 但很明显这个 strcmp 肯定是为 false 的, s1 是一个局部临时变量, 且输入被严格控制
if ( strcmp(&s1, "Nice_Try_But_The_Dragons_Won't_Let_You!") )
{
    puts("Wrong!\n");
    exit(-1);
}
system("/bin/sh");

可以看到, 基本是绕不过这个 if 语句的, 那么我就设想了要么是直接通过重定向到 system 所在的地址, 要么就是通过 UAF(Use After Free, 没见过的请 Google, 对于这题来说很重要) 这类漏洞来保留那段 Nice Try 密码. 不过当时我还是倾向于前者, 所以先以这个为目标来检查这个程序.

现在的问题是注入点在哪里, 因为程序还是不小的, 我就不一一附上伪代码了, 但如果你自己研究过这道题, 会发现本题很多检查都很严格, 找不到如修改 EBP 这类的重定向, 但我还是注意到了一些特别的地方:

// v3 是代表之前的 XXAttack 函数的结果, 进入则代表龙被杀了
if ( v3 )
{
    puts("Well Done Hero! You Killed The Dragon!");
    puts("The World Will Remember You As:");
    v2 = malloc(0x10u);
    __isoc99_scanf("%16s", v2);
    puts("And The Dragon You Have Defeated Was Called:");
    // 这里有个隐蔽的 UAF 漏洞, 这个 v5 在进入前已经在对应的 attack 函数内被 free 了, 这里 v2 申请了和它一样的大小, 然后又输入内容, 而这里却直接调用了一个已经被 free 了的函数, 明显是个 UAF
    ((void (__cdecl *)(_DWORD *))*v5)(v5);
}

分析到这里, 应该已经可以大概猜到这里就是注入点了, 通过 UAF 可以很简单重定向到我们想要执行的 system 地址.

然后我开始分析杀龙的函数, 嗯..., 首先有两种龙, 分别是 Babe dragon 和 Mama dragon, 前者只有 50 血但每次可以造成 30 点伤害, 而且每回合回血 5; 后者有 80 点血, 每次可以造成 10 点伤害, 且每回合回血 4. 然后存在两种角色, 分别是 Priest(牧师)和 Knight(骑士), 前者有 42 HP 和 50 MP, 有三种操作选项, 分别是攻击(消耗10MP, 造成 20 伤害), 恢复(满 MP, 但会被龙攻击), 圣盾(免疫一回合, 消耗25MP); 后者则有 50 HP, 没有 MP, 有两种攻击选项, 分别是平砍(造成20伤害)和重击(造成40伤害, 但会自损20血, 很蠢).

游戏部分的分析就差不多了, 那如果要进入到上述那个留名的函数, 则必须要龙被杀死, 嗯... 用这么渣的角色真的能杀死吗? 我排列组合了一下, 因为这题还有个限定条件, 就是会先判定人的血量, 如果低于0就直接输了, 所以不存在和龙同归于尽的机会, 但我排列组合了很多都是与龙同归于尽. 这时, 我突然想起了之前做过的那道 Black jack 题目, 会不会这里也存在这种负数溢出问题呢? 而且我也觉得牧师后两个技能很蠢, 然后我就看到了下面这个细节(超级容易被忽略):

do {
    // 游戏逻辑
}
while ( *((_BYTE *)ptr + 8) > 0 ); // ptr + 8 代表了龙的血量, 但要注意它转换成什么类型了

整个打龙的逻辑都包含在这个 do-while 循环中, 但是你要留意, 这点很关键, 那个 ptr 是被转换成 (BYTE) 来进行正负比较的.

估计我写到这里还是有人不知道我在说什么, 有符号的 BYTE 的表示范围是(127~-128), 嗯... 还看不懂, 那么如果龙的血量超过了 127 的话, 它就会变成负数(计算机是这么理解的), 负数是不是小于 0, 这样就会跳出这个循环了(代表龙死了), 然后就进入了我们想要进入的留名函数了. 那么有人会问, 怎么让龙涨血呢? 这里我最后提示一点, 多用牧师的恢复和圣盾这两个神技, 可以创造奇迹 ; P

提示部分就到这里了, 我觉得提示到这里基本就是能做出来了, 剩下的就只是重定向相关的问题了, 我觉得做到这个级别了, 应该没问题了.

答案

解答步骤和 Writeup 可以在我的 Github 中找到: JackoQm's Github: dragon writeup

上一篇 下一篇

猜你喜欢

热点阅读