# LACTF
## rev/javascryption
```javascript
const msg = document.getElementById("msg");
const flagInp = document.getElementById("flag");
const checkBtn = document.getElementById("check");
function checkFlag(flag) {
const step1 = btoa(flag);
const step2 = step1.split("").reverse().join("");
const step3 = step2.replaceAll("Z", "[OLD_DATA]");
const step4 = encodeURIComponent(step3);
const step5 = btoa(step4);
return step5 === "JTNEJTNEUWZsSlglNUJPTERfREFUQSU1RG85MWNzeFdZMzlWZXNwbmVwSjMlNUJPTERfREFUQSU1RGY5bWI3JTVCT0xEX0RBVEElNURHZGpGR2I=";
}
checkBtn.addEventListener("click", () => {
const flag = flagInp.value.toLowerCase();
if (checkFlag(flag)) {
flagInp.remove();
checkBtn.remove();
msg.innerText = flag;
msg.classList.add("correct");
} else {
checkBtn.classList.remove("shake");
checkBtn.offsetHeight;
checkBtn.classList.add("shake");
}
});
```
```
step5 = JTNEJTNEUWZsSlglNUJPTERfREFUQSU1RG85MWNzeFdZMzlWZXNwbmVwSjMlNUJPTERfREFUQSU1RGY5bWI3JTVCT0xEX0RBVEElNURHZGpGR2I=
step4 = %3D%3DQflJX%5BOLD_DATA%5Do91csxWY39VespnepJ3%5BOLD_DATA%5Df9mb7%5BOLD_DATA%5DGdjFGb
step3 = ==QflJX[OLD_DATA]o91csxWY39VespnepJ3[OLD_DATA]f9mb7[OLD_DATA]GdjFGb
step2 = ==QflJXZo91csxWY39VespnepJ3Zf9mb7ZGdjFGb
step1 = lactf{no_grizzly_walls_here}
```
## rev/patricks-paraflag
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // rbx
size_t v4; // rcx
size_t v5; // rax
int v6; // ebx
char v8[256]; // [rsp+0h] [rbp-208h] BYREF
char s[264]; // [rsp+100h] [rbp-108h] BYREF
printf("What do you think the flag is? ");
fflush(_bss_start);
fgets(s, 256, stdin);
v3 = strcspn(s, "\n");
s[v3] = 0;
if ( strlen(target) == v3 )
{
v4 = v3 >> 1;
if ( v3 > 1 )
{
v5 = 0LL;
do
{
v8[2 * v5] = s[v5];
v8[2 * v5 + 1] = s[v4 + v5];
++v5;
}
while ( v5 < v4 );
}
v8[v3] = 0;
printf("Paradoxified: %s\n", v8);
v6 = strcmp(target, v8);
if ( v6 )
{
puts("You got the flag wrong >:(");
return 0;
}
else
{
puts("That's the flag! :D");
}
}
else
{
puts("Bad length >:(");
return 1;
}
return v6;
}
```
```python
s = "l_alcotsft{_tihne__ifnlfaign_igtoyt}"
for i in range(0, len(s), 2):
print(s[i], end = '')
for i in range(1, len(s), 2):
print(s[i], end = '')
# output: lactf{the_flag_got_lost_in_infinity}
```
## rev/nine-solves
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 i; // rsi
unsigned int v4; // eax
int v5; // ecx
int v6; // edx
char input[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(input, 16, stdin);
for ( i = 0LL; i != 6; ++i )
{
v4 = input[i];
if ( (unsigned __int8)(input[i] - 32) > 0x5Eu ) //(1)
goto LABEL_14;
v5 = yi[i]; //(2)
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(yi);
return 0;
}
LABEL_14:
puts("ACCESS DENIED");
return 1;
}
```

* eigong là hàm gọi in ra file flag.txt trên server.
* LABEL_14 là đoạn exit, phải làm cho chương trình không được nhảy đến đây.
* (1): check kí tự in được bé hơn 127
* (2): yi là mảng gồm 6 số nguyên
* Đặt v4 là input[i], sau khi phân tích kĩ thì ta thấy chương trình đang đem v4 đi thực hiện liên tục các thao tác: nếu đang chẵn thì / 2, nếu lẻ thì * 3 + 1 cho đến khi v4 bằng 1. Số lần thực hiện các thao tác này (v6) phải bằng với yi[i].
* Coi mỗi số là một nút của đồ thị và một phép biến đổi là 1 cạnh có hướng. Vì mỗi nút có đúng 1 cạnh đi ra (trừ nút 1) nên đây là một DAG (hoặc có thể xem như một cây gốc 1).
* Vậy ta sẽ hoàn toàn có thể sinh ra cái cây này, gọi f[u] là độ dài đường đi từ nút u đến nút 1 (hay độ sâu của nút u trên cây), khi đó chỉ cần lấy ra các giá trị u sao cho f[u] bằng lần lượt các giá trị trong mảng yi là được (có thể có nhiều u).
```cpp
#include <bits/stdc++.h> //Logm
using namespace std;
map<int, int> f;
int calc(int x) {
if (x == 1) return 0;
if (f[x] != 0) return f[x];
if (x % 2 == 0) return f[x] = calc(x >> 1) + 1;
return f[x] = calc(x * 3 + 1) + 1;
}
void build() {
f[1] = 0;
for (int i = 2; i < 128; ++i) {
f[i] = calc(i);
// cout << i << ' ' << f[i] << endl;
}
}
vector<int> a[6];
void backtrack(int i, string s) {
if (i == 6) {
cout << s << endl;
return;
}
for (char c: a[i])
backtrack(i + 1, s + c);
}
signed main() {
build();
int yi[] = {27, 38, 87, 95, 118, 9};
for (int i = 0; i < 6; ++i)
for (int j = 33; j < 128; ++j)
if (f[j] == yi[i])
a[i].push_back(j);
backtrack(0, "");
getchar();
return 1;
}
```
output:
```
AigyaP
AigyaT
AigyaU
BigyaP
BigyaT
BigyaU
CigyaP
CigyaT
CigyaU
```
thử cái đầu tiên lên server thì đúng luôn

## rev/the-eye

Mỗi lần chạy đều in ra một hoán vị khác nhau của một đoạn văn gì đó (vì thấy số lần xuất hiện của các kí tự giống nhau, có cả {} nên chắc là có flag).
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
__int64 v4; // rdi
char *s; // [rsp+0h] [rbp-10h]
int i; // [rsp+Ch] [rbp-4h]
v3 = time(0LL);
v4 = v3;
srand(v3);
s = (char *)read_msg(v4, argv);
for ( i = 0; i <= 21; ++i )
shuffle(s);
puts(s);
free(s);
return 0;
}
```
```c
__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;
}
```
Khá rõ ràng là bài này sẽ lấy thời gian hiện tại làm seed để random shuffle cho một messenger chứa flag.
Vậy chỉ cần làm ngược lại là xong.
```python
from pwn import *
target = remote("chall.lac.tf", 31313)
from ctypes import CDLL
libc = CDLL('libc.so.6')
timestamp = int(time.time())
libc.srand(timestamp)
# print(timestamp)
s = target.recvline()
# print(s)
s = list(s)
if s[-1] == 10:
s.pop()
rd = []
def get():
n = len(s)
for i in range(n):
rd.append(libc.rand())
# def shuffle():
# n = len(s)
# i = n - 1
# while i >= 0:
# j = libc.rand() % (i + 1)
# s[i], s[j] = s[j], s[i]
# i -= 1
def rev_shuffle():
n = len(s)
for i in range(n):
j = rd[-1] % (i + 1)
rd.pop()
s[i], s[j] = s[j], s[i]
for i in range(22):
get()
for i in range(22):
rev_shuffle()
for i in s: print(chr(i), end = '')
target.interactive()
```

## rev/crypt-of-the-necropuzzler
```python
#!/usr/local/bin/python3
import tty
import sys
import hashlib
if sys.stdin.isatty():
tty.setcbreak(0)
g=(f:=[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1])[:]
n=[1,1,0,0,0,0,0,0,1,0,2,0,0,0,0,0,3,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,3,0,0,0,1,0,1,0,1,0,0,0,2,2,2,0,0]
def decrypt_flag(k):
h=hashlib.sha512(str(k).encode()).digest()
print(bytes(a^b for(a,b)in zip(h,bytes.fromhex("8b1e35ac3da64cb9db365e529ad8c9496388a4f499faf887386b4f6c43b616aae990f17c1b1f34af514800275673e0f3c689c0998fc73c342f033aa7cc69d199"))).decode())
m={'w':(-1,0),'s':(1,0),'a':(0,-1),'d':(0,1)}
def t(a,b,s=None):
if s is None:
s = set()
s.add((a,b))
for(i,j)in m.values():
x,y=a+i,b+j
if (x,y) not in s and x in range(7) and y in range(7) and g[x*7+y]==g[a*7+b]:
t(x,y,s)
return s
a,b=0,0
d=1
while 1:
if d:
print("\x1b[2J")
for i in range(7):
print(" ["[(a,b)==(i,0)],end="")
for j in range(7):
print("_#"[g[i*7+j]],end="["if(a,b)==(i,j+1)else" ]"[(a,b)==(i,j)])
print()
d=0
try:
c=sys.stdin.read(1)
if c == "":
break
except EOFError:
break
if c=='q':
break
elif c=='x':
if not f[i:=a*7+b]:
g[i]=1-g[i]
d=1
elif v:=m.get(c):
i,j=a+v[0],b+v[1]
if i in range(7) and j in range(7):
a,b=i,j
d=1
elif c=='c':
p=1
s=set()
for i in range(7):
for j in range(7):
if(i,j)not in s:
v=[0]*4
k=t(i,j)
s|=k
for(x,y)in k:
v[n[x*7+y]]+=1
if any(h not in (0,2) for h in v[1:]):
p=0 #(*1)
if p:
print("Correct!")
decrypt_flag(g)
else:
print("Incorrect!")
```

Dạng như là một game trên bảng có các chức năng duy chuyển bằng w,a,s,d và x để bật tắt một ô, c để check và q để thoát.
Vậy giờ ta cần làm cho bảng đúng theo trạng thái nào đó để khi check thì in ra được flag, nghĩa là chỗ (*1) ko được chạy ==> trong v[1], v[2], v[3] tất cả các phần tử phải bằng 0 hoặc 2.
Phân tích:
* bảng đang chơi là mảng g, f là mảng cố định (những vị trí # hay 1 sẽ không thể thay đổi).
* bảng sẽ liên tục được cập nhật và in ra màn hình
* Check:
* Hàm t(i, j) sẽ lấy ra một tập các tọa độ cùng vùng liên thông và cùng giá trị với ô (i, j).
* Nhóm đó sẽ dần được dồn vào s đễ không bị duyệt lại nhiều lần.
* Khi có một nhóm mới sẽ tăng v[n[x*7+y]] với (x, y) là các tọa độ của nhóm mới, n là mảng hằng, sau đó thực hiện kiểm tra luôn. Vì không quan tâm đến v[0], ta cần chú ý đến những chỗ khác 0 trong mảng n. Tất cả các vị trí trước sau gì cũng sẽ được update, vấn đề ở update ở tplt nào, vì mỗitplt thì mảng v sẽ reset lại nên giờ mình cần tìm cách chia nhóm mảng n sao cho thỏa mãn (đảm bảo trong một nhóm chỉ được có 0 hoặc 2 số giống nhau không tính số 0).

puzzle của chúng ta sẽ là tìm cách tô màu các ô còn lại sao cho với mỗi nhóm các ô cùng màu kề nhau thì từng số khác 0 phải xuất hiện 0 hoặc 2 lần.

Cảm ơn bạn Hùng :3
```python
import hashlib
g=[0,0,1,1,1,1,1,
1,1,1,0,0,0,1,
1,0,0,1,1,0,1,
1,0,1,0,1,0,1,
1,0,1,1,0,0,1,
1,0,0,0,0,1,1,
1,1,1,0,1,1,1]
n=[1,1,0,0,0,0,0,
0,1,0,2,0,0,0,
0,0,3,1,0,0,0,
0,0,1,0,1,0,0,
0,0,0,1,3,0,0,
0,1,0,1,0,1,0,
0,0,2,2,2,0,0]
def decrypt_flag(k):
h=hashlib.sha512(str(k).encode()).digest()
print(bytes(a^b for(a,b)in zip(h,bytes.fromhex("8b1e35ac3da64cb9db365e529ad8c9496388a4f499faf887386b4f6c43b616aae990f17c1b1f34af514800275673e0f3c689c0998fc73c342f033aa7cc69d199"))).decode())
m={'w':(-1,0),'s':(1,0),'a':(0,-1),'d':(0,1)}
def t(a,b,s=None):
if s is None:
s = set()
s.add((a,b))
for(i,j)in m.values():
x,y=a+i,b+j
if (x,y) not in s and x in range(7) and y in range(7) and g[x*7+y]==g[a*7+b]:
t(x,y,s)
return s
p=1
s=set()
for i in range(7):
for j in range(7):
if(i,j)not in s:
v=[0]*4
k=t(i,j)
s|=k
for(x,y)in k:
v[n[x*7+y]]+=1
if any(h not in (0,2) for h in v[1:]):
p=0
if p:
print("Correct!")
decrypt_flag(g)
else:
print("Incorrect!")
```
