---
# System prepended metadata

title: DiceCTF 2025 Quals

---

# DiceCTF 2025 Quals 
> Cảm ơn tất cả mọi người trong team vì đã tryhard tới phút cuối cùng. Shout out cho bạn web team mình vì bạn quỷ vl (đấm hẳn 4 challs web, how?).

![image](https://hackmd.io/_uploads/B1KOiIDpke.png)
## Rev/debugalyzer
Đề bài cho 2 files  `dwarf` và `main`.
``` linux
~/currentCTF
❯ file dwarf
dwarf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ea8b6ed84558414de1165e3b6516d706cba825a0, for GNU/Linux 4.4.0, not stripped

~/currentCTF
❯ file main
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f89920a47f694838ae74c88db45a421949478a92, for GNU/Linux 4.4.0, with debug_info, not stripped

~/currentCTF
❯ ./main
debug me!
```
Bật IDA mở file `main` ra đọc thì thấy nó khá vô nghĩa, nên mình chuyển sang file `dwarf`.
Chạy thử:
``` linux
~/currentCTF
❯ ./dwarf
Usage: ./dwarf <elf-file>

~/currentCTF
❯ ./dwarf main
Processing...
Processing .debug_line unit: total_size=1690, header_size=64, instructions_length=1626
Flag is incorrect!
```
Mở IDA ra rev thì trong đó có một hàm khiến mình chú ý là `execute_dwarf_bytecode_v4`.
![image](https://hackmd.io/_uploads/BygrpLv6Jl.png)
```c
int __fastcall execute_dwarf_bytecode_v4(__int64 a1, __int64 a2, __int64 a3, __int64 a4, unsigned __int8 a5)
{
  __int64 v5; // rax
  __int64 v7; // r13
  unsigned __int8 v8; // al
  __int64 v9; // r12
  __int64 v10; // rbp
  __int64 v11; // rdx
  unsigned __int8 v12; // bl
  char *v13; // rsi
  unsigned __int64 v14; // rcx
  int v15; // eax
  const char *v16; // rax
  __int64 v18; // rax
  int v19; // eax
  char v20; // si
  char v21; // bp
  int v22; // eax
  __int64 v23; // rdx
  char v24; // si
  __int64 v25; // rcx
  unsigned __int8 v26; // di
  unsigned __int8 v27; // si
  int v28; // eax
  __int64 v29; // rdx
  unsigned __int8 v30; // si
  char v31; // al
  unsigned __int64 v32; // [rsp+0h] [rbp-58h]
  unsigned int check; // [rsp+8h] [rbp-50h]
  __int64 v35; // [rsp+10h] [rbp-48h] BYREF
  __int64 v36[8]; // [rsp+18h] [rbp-40h] BYREF

  v36[0] = a1;
  v35 = a2;
  if ( !a2 )
  {
    check = 1;
    goto LABEL_15;
  }
  v5 = a2;
  check = 1;
  while ( 1 )
  {
    v11 = v36[0];
    v12 = *(_BYTE *)v36[0];
    v13 = (char *)++v36[0];
    v14 = v5 - 1;
    v35 = v5 - 1;
    if ( v12 )
    {
      if ( v12 < *(_BYTE *)(a3 + 3) )
      {
        switch ( v12 )
        {
          case 1u:
          case 6u:
          case 7u:
          case 8u:
          case 0xAu:
          case 0xBu:
            goto LABEL_9;
          case 2u:
          case 4u:
          case 5u:
          case 0xCu:
            read_uleb128(v36, &v35);
            break;
          case 3u:
            do
            {
              v31 = *v13++;
              --v14;
            }
            while ( v31 < 0 && v14 );
            v36[0] = (__int64)v13;
            v35 = v14;
            break;
          case 9u:
            if ( v14 > 1 )
            {
              v36[0] = v11 + 3;
              v35 = v5 - 3;
            }
            break;
          default:
            printf("Opcode %d unimplemented\n", v12);
            break;
        }
      }
      goto LABEL_9;
    }
    v32 = read_uleb128(v36, &v35);
    v7 = v35;
    if ( !v35 )
      goto LABEL_15;
    v8 = *(_BYTE *)v36[0];
    v9 = ++v36[0];
    v10 = --v35;
    if ( v8 == 81 )
    {
      v19 = read_uleb128(v36, &v35);
      if ( !v35 )
        goto LABEL_15;
      v20 = *(_BYTE *)v36[0]++;
      --v35;
      flag[v19] = v20;
      goto LABEL_9;
    }
    if ( v8 <= 0x51u )
      break;
    if ( v8 != 82 )
      goto LABEL_34;
    v15 = read_uleb128(v36, &v35);
    if ( !v35 )
      goto LABEL_15;
    v21 = flag[v15];
    v22 = read_uleb128(v36, &v35);
    v23 = v35;
    if ( !v35 )
      goto LABEL_15;
    v24 = flag[v22];
    v25 = v36[0];
    v26 = *(_BYTE *)v36[0];
    --v35;
    if ( v23 == 1 )
      goto LABEL_15;
    if ( v26 == 2 )
    {
      v12 = v24 * v21;
    }
    else if ( v26 > 2u )
    {
      v30 = v21 ^ v24;
      if ( v26 == 3 )
        v12 = v30;
    }
    else
    {
      v12 = v21 - v24;
      v27 = v21 + v24;
      if ( !v26 )
        v12 = v27;
    }
    v36[0] += 2LL;
    v28 = v36[0];
    v29 = v23 - 2;
    v35 = v29;
    LOBYTE(v28) = v29 != 0;
    LOBYTE(v29) = v12 != *(_BYTE *)(v25 + 1);
    check &= v29 & v28 ^ 1;
LABEL_9:
    v5 = v35;
    if ( !v35 )
      goto LABEL_15;
  }
  if ( v8 == 1 )
    goto LABEL_9;
  if ( v8 == 2 )
  {
    if ( v32 >= (unsigned __int64)a5 + 1 )
    {
      if ( a5 )
      {
        v18 = 0LL;
        do
          ++v18;
        while ( a5 != v18 );
      }
      else
      {
        v18 = 0LL;
      }
      v36[0] = v18 + v9;
      v35 = v10 - v18;
    }
    goto LABEL_9;
  }
LABEL_34:
  printf("Extended opcode %d unimplemented\n", 0LL);
  if ( v32 <= 1 )
    goto LABEL_9;
  while ( v10 )
  {
    if ( v10 - 1 == v7 - v32 )
    {
      v35 = v10 - 1;
      v36[0] = v7 + v9 - v10;
      goto LABEL_9;
    }
    --v10;
  }
LABEL_15:
  v16 = (const char *)select_str(incorrect_msg, correct_msg, check);
  return puts(v16);
}
```
Tới khúc này thì thấy nó là 1 cái VM. Tới đây thì mình có hướng như này:
![image](https://hackmd.io/_uploads/HkBkywvpyl.png)
![image](https://hackmd.io/_uploads/HkQeJDD61x.png)
Đặt breakpoint để lấy giá trị của `v15`, `v22`, `v12` và `*(v25+1)`, từ đó mình sẽ biết nó lấy giá trị của flag ở đâu, và nó thực hiện phép tính gì với flag.
``` gdb
*R8   0x13
*R9   0x13
 R10  0
 R11  0x202
 R12  0x55555555bb72 ◂— 0x520500f7011300
 R13  0x579
 R14  0x7fffffffd540 ◂— 0x10d0efb01
 R15  0x555555556164 ◂— 0xfffff277fffff53c
*RBP  0x64
 RSP  0x7fffffffd4a0 ◂— 5
*RIP  0x555555555518 (execute_dwarf_bytecode_v4+480) ◂— mov rdx, qword ptr [rsp + 0x10]
─────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
   0x5555555554fe <execute_dwarf_bytecode_v4+454>    lea    rdx, [rip + 0x2c3b]           RDX => 0x555555558140 (flag) ◂— 'dice{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}'
   0x555555555505 <execute_dwarf_bytecode_v4+461>    movzx  ebp, byte ptr [rdx + rax]     EBP, [flag] => 0x64
   0x555555555509 <execute_dwarf_bytecode_v4+465>    lea    rsi, [rsp + 0x10]             RSI => 0x7fffffffd4b0 ◂— 0x577
   0x55555555550e <execute_dwarf_bytecode_v4+470>    lea    rdi, [rsp + 0x18]             RDI => 0x7fffffffd4b8 —▸ 0x55555555bb73 ◂— 0x1700520500f70113
   0x555555555513 <execute_dwarf_bytecode_v4+475>    call   read_uleb128                <read_uleb128>

 ► 0x555555555518 <execute_dwarf_bytecode_v4+480>    mov    rdx, qword ptr [rsp + 0x10]     RDX, [0x7fffffffd4b0] => 0x576
   0x55555555551d <execute_dwarf_bytecode_v4+485>    test   rdx, rdx                        0x576 & 0x576     EFLAGS => 0x202 [ cf pf af zf sf IF df of ]
   0x555555555520 <execute_dwarf_bytecode_v4+488>    je     execute_dwarf_bytecode_v4+269 <execute_dwarf_bytecode_v4+269>

   0x555555555526 <execute_dwarf_bytecode_v4+494>    cdqe
   0x555555555528 <execute_dwarf_bytecode_v4+496>    lea    rcx, [rip + 0x2c11]           RCX => 0x555555558140 (flag) ◂— 'dice{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}'
   0x55555555552f <execute_dwarf_bytecode_v4+503>    movzx  esi, byte ptr [rcx + rax]     ESI, [flag+19] => 0x58
──────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd4a0 ◂— 5
01:0008│     0x7fffffffd4a8 ◂— 0x8007fff00000001
02:0010│ rsi 0x7fffffffd4b0 ◂— 0x576
03:0018│ rdi 0x7fffffffd4b8 —▸ 0x55555555bb74 ◂— 0x11700520500f701
04:0020│     0x7fffffffd4c0 ◂— 3
05:0028│     0x7fffffffd4c8 ◂— 0x65a
06:0030│     0x7fffffffd4d0 ◂— 0x40 /* '@' */
07:0038│     0x7fffffffd4d8 —▸ 0x55555555ba50 ◂— 0x36000400000696
────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
 ► 0   0x555555555518 execute_dwarf_bytecode_v4+480
   1   0x555555555a2f main+873
   2   0x7ffff7da31ca __libc_start_call_main+122
   3   0x7ffff7da328b __libc_start_main+139
   4   0x5555555551a5 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg>
```
Debug thử thì thấy flag nó mặc định luôn là:
``` gdb
pwndbg> x/s 0x555555558140
0x555555558140 <flag>:  "dice{", 'X' <repeats 34 times>, "}"
```
Mình có thử mò ngồi thay đổi giá trị này, nhưng kết quả thì không khả quan lắm. 
Quay lại vấn đề đặt breakpoint, thì mình viết script gdb thôi.
``` gdb
start main
breakrva 0x1A2A 
breakrva 0x1439
breakrva 0x1518 
breakrva 0x1576
while (1)
    c
    p/x $rax 
    c 
    p/x $rax 
    c 
    x/b $rcx + 1
    p/x $bl
end
```
Ném log vào file output, xong rồi rev lại các phép tính.
``` python
#!/usr/bin/python3 
from pwn import * 
from z3 import * 

data = open("output.txt", "r").read().split('\n')
start = 34 
solver = Solver()
flag = b'dice{' + b'X' * 34 + b'}'
ans = list(b'dice{') + [0] * 34 + list(b'}')
for i in range(200):
    a = data[start]
    b = data[start + 3]
    c = data[start + 6]
    d = data[start + 7]
    a = a[a.find('=') + 1:].strip()
    b = b[b.find('=') + 1:].strip()
    c = c[c.find(':') + 1:].strip()
    d = d[d.find('=') + 1:].strip()
    a = int(a, 16)
    b = int(b, 16)
    c = int(c, 16)
    d = int(d, 16)
    if (flag[a] + flag[b]) & 0xff == d:
        if ans[b] == 0:
            ans[b] = (c - ans[a]) & 0xff
        elif ans[a] == 0:   
            ans[a] = (c - ans[b]) & 0xff
    if (flag[a] ^ flag[b]) & 0xff == d and (a != 9 and b != 9) and (a != 18 and b != 18) and (a != 26 and b != 26) and (a != 29 and b != 29):
        if ans[b] == 0:
            ans[b] = (ans[a] ^ c) & 0xff
        elif ans[a] == 0:   
            ans[a] = (c ^ ans[b]) & 0xff
    if (flag[a] - flag[b]) & 0xff == d:
        if ans[b] == 0:
            ans[b] = (ans[a] - c) & 0xff
        elif ans[a] == 0: 
            ans[a] = (c + ans[b]) & 0xff


    if (flag[a] * flag[b]) & 0xff == d:
        if ans[b] == 0:
            for t in range(33, 127):
                if (t * ans[a]) & 0xff == c:
                    ans[b] = t
                    break
        elif ans[a] == 0:
            for t in range(33, 127):
                if (t * ans[b]) & 0xff == c:
                    ans[a] = t
                    break
    start += 10

print(bytes(ans))
# dice{h1ghly_sp3c_c0mpl14nt_dw4rf_p4rs3r}
```
**Còn một vấn đề nữa mà script của mình gặp**: Vì mình không thể thay đổi xâu `flag` trên kia, nên `X - X = X ^ X` thì lúc đó rev nó sẽ bị sai. Đoạn này mình thực sự phải chỉnh script cho tới khi nào ra `flag`.
## Rev/ono
```linux
~/currentCTF 8m
❯ ./ono
oño? supershy
oño.
```
Hmm có vẻ đây là một bài flag checker, lets go mở IDA lên thôi nào.
Hàm `main`:
``` c++
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  size_t v3; // rax
  __int64 v4; // rbx
  __int64 v5; // rbp
  char *v6; // rdi
  __int64 v7; // rcx
  int v8; // ebp
  __int64 i; // rax
  int v10; // edx
  char *v12[2]; // [rsp+0h] [rbp-F8h] BYREF
  char v13; // [rsp+17h] [rbp-E1h] BYREF
  char v14; // [rsp+2Ch] [rbp-CCh] BYREF
  char v15; // [rsp+41h] [rbp-B7h] BYREF
  char v16[26]; // [rsp+56h] [rbp-A2h] BYREF
  char *argv[4]; // [rsp+70h] [rbp-88h] BYREF
  char s[8]; // [rsp+90h] [rbp-68h] BYREF
  __int64 v19[12]; // [rsp+98h] [rbp-60h]

  printf(format, a2, a3);
  fflush(stdout);
  fgets(s, 64, stdin);
  v3 = strcspn(s, "\n");
  s[v3] = 0;
  if ( v3 == 32 )
  {
    v4 = *(_QWORD *)s;
    argv[3] = 0LL;
    argv[0] = &v13;
    v5 = 0LL;
    argv[1] = &v14;
    argv[2] = &v15;
    v12[0] = v16;
    v12[1] = 0LL;
    do
    {
      v6 = argv[v5];
      v7 = v19[v5++];
      snprintf(v6, 0x15uLL, "%llu", v7);
    }
    while ( v5 != 3 );
    v8 = memfd_create(&unk_200F, 0LL);
    for ( i = 0LL; i != 5429; ++i )
    {
      while ( 1 )
      {
        qword_4080[i] = v4 ^ (0x4C01DB400B0C9LL * qword_4080[i]);
        v10 = 32;
        do
        {
          v4 = __ROL8__((37 * v4) ^ (42424242 * v4), 7);
          --v10;
        }
        while ( v10 );
        if ( i )
          break;
        snprintf(v16, 0x1AuLL, aO, v4);
        i = 1LL;
      }
    }
    write(v8, qword_4080, 0xA9A8uLL);
    fexecve(v8, argv, v12);
  }
  puts(::s);
  return 1LL;
}
```
Vậy là flow nó như này:
- Check input có thỏa 32 kí tự không
- Xong r nó gán `v4 = 8 bytes đầu của flag` xong thực hiện 1 đống phép tính gì đó.
- Đồng thời nó cũng gọi `snprintf(v16, 0x1AuLL, "oño=%llu", v4);`. Đoạn này nó sẽ dùng để gán vào `envp (v12[0] = v16, v12[1] = 0)`. 
- Xong rồi nó write data từ một cái mảng mà ta thực hiện các phép tính abcxyz gì đó vào `v8`, `write(v8, qword_4080, 0xA9A8uLL);`
- Cuối cùng thì nó gọi `fexecve(v8, argv, v12);`.

Ở đây thì mình đoán **8 bytes đầu** của **qword_4080** sau khi tính toán sẽ là header của một file ELF. Giờ viết script để tìm v4 thôi lets go.
``` python
#!/usr/bin/python3 
from z3 import * 
from pwn import * 

a = 0x7F861CD6D160EBC3
header = u64(bytes.fromhex('7f45 4c46 0201 0100'))
v4 = BitVec('v4', 64)
solver = Solver()
solver.add(v4 ^ (0x4C01DB400B0C9 * a) == header)
if solver.check() == sat:
    m = solver.model()
    ans = m[v4].as_long()
    print(hex(ans))
    print(p64(ans))
```
``` linux
~/currentCTF 44s
❯ ./solve.py
0x77306e7b65636964
b'dice{n0w'
```
Vậy đây là 8 bytes đầu của flag, giờ mình dump memory ra một file bin xem nó sẽ `fexecve` cái gì.
Để dump memory ra thì ta xài lệnh này trong gdb:
``` linux
dump binary memory dump.bin 0x555555558080 0x555555558080+0xa9a8
```
``` gdb
pwndbg> breakrva 0x1223
Breakpoint 2 at 0x555555555223
pwndbg> c
Continuing.
oño? dice{n0waaaaaaaaaaaaaaaaaaaaaaaa

Breakpoint 2, 0x0000555555555223 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────
*RAX  0x1535
*RBX  0x3a789197de462f9f
*RCX  0x84721106af655b7d
*RDX  0xa9a8
*RDI  3
*RSI  0x555555558080 ◂— 0x10102464c457f
*R8   0x75
*R9   0
*R10  0
*R11  0
*R12  0x7fffffffd576 ◂— 0x3439383d6fb1c36f
*R13  0x7fffffffd590 —▸ 0x7fffffffd537 ◂— '7016996765293437281'
*R14  0x555555558080 ◂— 0x10102464c457f
*R15  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP  3
*RSP  0x7fffffffd520 —▸ 0x7fffffffd576 ◂— 0x3439383d6fb1c36f
*RIP  0x555555555223 ◂— call 0x555555555050
──────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────
 ► 0x555555555223    call   write@plt                   <write@plt>
        fd: 3 (/memfd:oño (deleted))
        buf: 0x555555558080 ◂— 0x10102464c457f
        n: 0xa9a8

   0x555555555228    mov    rdx, rsp
   0x55555555522b    mov    rsi, r13
   0x55555555522e    mov    edi, ebp
   0x555555555230    call   fexecve@plt                 <fexecve@plt>

   0x555555555235    lea    rdi, [rip + 0xdcd]     RDI => 0x555555556009 ◂— 0xc36f002e6fb1c36f
   0x55555555523c    call   puts@plt                    <puts@plt>

   0x555555555241    push   1
   0x555555555243    pop    rax
   0x555555555244    add    rsp, 0xd0
   0x55555555524b    pop    rbx
───────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd520 —▸ 0x7fffffffd576 ◂— 0x3439383d6fb1c36f
01:0008│     0x7fffffffd528 ◂— 0
02:0010│     0x7fffffffd530 ◂— 0x3700000000000002
03:0018│     0x7fffffffd538 ◂— '016996765293437281'
04:0020│     0x7fffffffd540 ◂— '5293437281'
05:0028│     0x7fffffffd548 ◂— 0x3631303700003138 /* '81' */
06:0030│     0x7fffffffd550 ◂— '996765293437281'
07:0038│     0x7fffffffd558 ◂— 0x31383237333433 /* '3437281' */
─────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► 0   0x555555555223
   1   0x7ffff7dc21ca __libc_start_call_main+122
   2   0x7ffff7dc228b __libc_start_main+139
   3   0x555555555281
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/s 0x555555558080
0x555555558080: "\177ELF\002\001\001"
pwndbg> dump binary memory ono_dump_1.bin 0x555555558080 0x555555558080+0xa9a8
```
``` linux
~/currentCTF 2m 35s
❯ file ono_dump_1.bin
ono_dump_1.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped
```
Mở `ono_dump_1.bin` ra xem thử:
![image](https://hackmd.io/_uploads/BJ_ucDPpJg.png)
Mình chú ý logic chính là đoạn này:
```c++
    v8 = memfd_create(&unk_2006, 0LL);
    v9 = (__int64 *)&unk_4060;
    do
    {
      v10 = *v9;
      v9 = (__int64 *)((char *)v9 + 1);
      *(__int64 *)((char *)v9 - 1) = v5 * v10;
    }
    while ( v9 != (__int64 *)((char *)&unk_4060 + 28968) );
    write(v8, &unk_4060, 0x7130uLL);
    v11 = (__int64)*a3;
    envp[0] = s;
    envp[1] = 0LL;
    __isoc99_sscanf(v11 + 5, "%llu", &v14);
    v14 ^= v5;
    snprintf(s, 0x1AuLL, format);
    fexecve(v8, a2 + 1, envp);
```
Viết script để tìm tiếp thôi nhỉ:
``` python
#!/usr/bin/python3 
from z3 import * 
from pwn import * 

x = u64(bytes.fromhex('E1 AF 4B DF 9D C6 25 C1'))
header = u64(bytes.fromhex('7f45 4c46 0201 0100'))
v4 = BitVec('v4', 64)
solver = Solver()
for i in range(8):
    x *= v4 
    solver.add(x & 0xff == header & 0xff)
    header >>= 8
    x >>= 8
if solver.check() == sat:
    m = solver.model()
    ans = m[v4].as_long()
    print(hex(ans))
    print(p64(ans))
```
``` linux
~/currentCTF
❯ ./solve.py
0x30635f337233775f
b'_w3r3_c0'
```
Ra được 8 bytes tiếp theo của flag.
Dump memory các kiểu tiếp để xem nó `fexecve` cái gì tiếp thôi.
``` gdb
pwndbg> breakrva 0x1107
Breakpoint 2 at 0x555555555107
pwndbg> breakrva 0x1138
Breakpoint 3 at 0x555555555138
pwndbg> c
Continuing.

Breakpoint 2, 0x0000555555555107 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────
*RAX  0xf5b5e8549fa67db9
*RBX  0x7fffffffd728 —▸ 0x7fffffffd9a1 ◂— '/home/m1zuguchi/currentCTF/ono_dump_1.bin'
*RCX  0
*RDX  0x5cff9b68cc2
*RDI  0x7fffffffcf30 ◂— 0
*RSI  0x5cff9b68cc2
*R8   0x2f
*R9   0xffffffff
*R10  0xffffffffffffff88
*R11  0x7fffffffd3b0 ◂— 0xbaafbad8001
*R12  1
*R13  0x7fffffffd738 —▸ 0x7fffffffd9cb ◂— 'SHELL=/bin/bash'
*R14  0x555555556010 ◂— 0x756c6c25 /* '%llu' */
*R15  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP  0x8000
*RSP  0x7fffffffd590 ◂— 0x5cff9b68cc2
*RIP  0x555555555107 ◂— cmp rdx, rax
──────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────
 ► 0x555555555107    cmp    rdx, rax     0x5cff9b68cc2 - 0xf5b5e8549fa67db9     EFLAGS => 0x217 [ CF PF AF zf sf IF df of ]
   0x55555555510a  ✔ jne    0x5555555551ab              <0x5555555551ab>
    ↓
   0x5555555551ab    lea    rdi, [rip + 0xe4e]     RDI => 0x555555556000 ◂— 0xc36f002e6fb1c36f
   0x5555555551b2    call   puts@plt                    <puts@plt>

   0x5555555551b7    push   1
   0x5555555551b9    pop    rax
   0x5555555551ba    add    rsp, 0x48
   0x5555555551be    pop    rbx
   0x5555555551bf    pop    rbp
   0x5555555551c0    pop    r12
   0x5555555551c2    pop    r13
───────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd590 ◂— 0x5cff9b68cc2
01:0008│     0x7fffffffd598 —▸ 0x7fffffffd5c8 ◂— 0
02:0010│     0x7fffffffd5a0 ◂— 0x6000000017
03:0018│     0x7fffffffd5a8 ◂— 0
... ↓        4 skipped
─────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► 0   0x555555555107
   1   0x7ffff7dc21ca __libc_start_call_main+122
   2   0x7ffff7dc228b __libc_start_main+139
   3   0x5555555551f1
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> set $rdx=0xf5b5e8549fa67db9
pwndbg> c
Continuing.

Breakpoint 3, 0x0000555555555138 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────
*RAX  0x555555558061 ◂— 0xb9c125c69ddf4baf
 RBX  0x7fffffffd728 —▸ 0x7fffffffd9a1 ◂— '/home/m1zuguchi/currentCTF/ono_dump_1.bin'
*RCX  0xc125c69ddf4bafe1
*RDX  0x55555555f188 ◂— 0
*RDI  0x555555556006 ◂— 0xb1c36f006fb1c36f
*RSI  0x555555558060 ◂— 0xc125c69ddf4bafe1
 R8   0x2f
 R9   0xffffffff
*R10  0x7ffff7dad3d0 ◂— 0x110012000022e3
*R11  0x206
*R12  3
 R13  0x7fffffffd738 —▸ 0x7fffffffd9cb ◂— 'SHELL=/bin/bash'
 R14  0x555555556010 ◂— 0x756c6c25 /* '%llu' */
 R15  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
 RBP  0x8000
 RSP  0x7fffffffd590 ◂— 0x5cff9b68cc2
*RIP  0x555555555138 ◂— imul rcx, rbp
──────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────
 ► 0x555555555138    imul   rcx, rbp
   0x55555555513c    mov    qword ptr [rax - 1], rcx     [0x555555558060] => 0xe34eefa5d7f08000
   0x555555555140    cmp    rax, rdx                     0x555555558061 - 0x55555555f188     EFLAGS => 0x293 [ CF pf AF zf SF IF df of ]
   0x555555555143  ✔ jne    0x555555555132              <0x555555555132>
    ↓
   0x555555555132    mov    rcx, qword ptr [rax]         RCX, [0x555555558061] => 0xb9e34eefa5d7f080
   0x555555555135    inc    rax                          RAX => 0x555555558062
   0x555555555138   imul   rcx, rbp
   0x55555555513c    mov    qword ptr [rax - 1], rcx     [0x555555558061] => 0xa777d2ebf8400000
   0x555555555140    cmp    rax, rdx                     0x555555558062 - 0x55555555f188     EFLAGS => 0x293 [ CF pf AF zf SF IF df of ]
   0x555555555143  ✔ jne    0x555555555132              <0x555555555132>
    ↓
   0x555555555132    mov    rcx, qword ptr [rax]         RCX, [0x555555558062] => 0xc9a777d2ebf84000
───────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd590 ◂— 0x5cff9b68cc2
01:0008│     0x7fffffffd598 —▸ 0x7fffffffd5c8 ◂— 0
02:0010│     0x7fffffffd5a0 ◂— 0x6000000017
03:0018│     0x7fffffffd5a8 ◂— 0
... ↓        4 skipped
─────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► 0   0x555555555138
   1   0x7ffff7dc21ca __libc_start_call_main+122
   2   0x7ffff7dc228b __libc_start_main+139
   3   0x5555555551f1
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> set $rbp=0x30635f337233775f
pwndbg> ni
0x000055555555513c in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────
 RAX  0x555555558061 ◂— 0xb9c125c69ddf4baf
 RBX  0x7fffffffd728 —▸ 0x7fffffffd9a1 ◂— '/home/m1zuguchi/currentCTF/ono_dump_1.bin'
*RCX  0xe196d09b47aadb7f
 RDX  0x55555555f188 ◂— 0
 RDI  0x555555556006 ◂— 0xb1c36f006fb1c36f
 RSI  0x555555558060 ◂— 0xc125c69ddf4bafe1
 R8   0x2f
 R9   0xffffffff
 R10  0x7ffff7dad3d0 ◂— 0x110012000022e3
 R11  0x206
 R12  3
 R13  0x7fffffffd738 —▸ 0x7fffffffd9cb ◂— 'SHELL=/bin/bash'
 R14  0x555555556010 ◂— 0x756c6c25 /* '%llu' */
 R15  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
 RBP  0x30635f337233775f ('_w3r3_c0')
 RSP  0x7fffffffd590 ◂— 0x5cff9b68cc2
*RIP  0x55555555513c ◂— mov qword ptr [rax - 1], rcx
──────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────
   0x555555555140    cmp    rax, rdx
   0x555555555143    jne    0x555555555132              <0x555555555132>

   0x555555555132    mov    rcx, qword ptr [rax]
   0x555555555135    inc    rax
   0x555555555138    imul   rcx, rbp
 ► 0x55555555513c    mov    qword ptr [rax - 1], rcx     [0x555555558060] => 0xe196d09b47aadb7f
   0x555555555140    cmp    rax, rdx                     0x555555558061 - 0x55555555f188     EFLAGS => 0x293 [ CF pf AF zf SF IF df of ]
   0x555555555143  ✔ jne    0x555555555132              <0x555555555132>
    ↓
   0x555555555132    mov    rcx, qword ptr [rax]         RCX, [0x555555558061] => 0xb9e196d09b47aadb
   0x555555555135    inc    rax                          RAX => 0x555555558062
   0x555555555138    imul   rcx, rbp
───────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd590 ◂— 0x5cff9b68cc2
01:0008│     0x7fffffffd598 —▸ 0x7fffffffd5c8 ◂— 0
02:0010│     0x7fffffffd5a0 ◂— 0x6000000017
03:0018│     0x7fffffffd5a8 ◂— 0
... ↓        4 skipped
─────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► 0   0x55555555513c
   1   0x7ffff7dc21ca __libc_start_call_main+122
   2   0x7ffff7dc228b __libc_start_main+139
   3   0x5555555551f1
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> info break
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000555555555107
        breakpoint already hit 1 time
3       breakpoint     keep y   0x0000555555555138
        breakpoint already hit 1 time
pwndbg> del 3
pwndbg> breakrva 0x1152
Breakpoint 4 at 0x555555555152
pwndbg> c
Continuing.

Breakpoint 4, 0x0000555555555152 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────
*RAX  0x55555555f188 ◂— 0
 RBX  0x7fffffffd728 —▸ 0x7fffffffd9a1 ◂— '/home/m1zuguchi/currentCTF/ono_dump_1.bin'
*RCX  0
*RDX  0x7130
*RDI  3
 RSI  0x555555558060 ◂— 0x10102464c457f
 R8   0x2f
 R9   0xffffffff
 R10  0x7ffff7dad3d0 ◂— 0x110012000022e3
 R11  0x206
 R12  3
 R13  0x7fffffffd738 —▸ 0x7fffffffd9cb ◂— 'SHELL=/bin/bash'
 R14  0x555555556010 ◂— 0x756c6c25 /* '%llu' */
*R15  0x7fffffffd5b6 ◂— 0
 RBP  0x30635f337233775f ('_w3r3_c0')
 RSP  0x7fffffffd590 ◂— 0x5cff9b68cc2
*RIP  0x555555555152 ◂— call 0x555555555050
──────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────
 ► 0x555555555152    call   write@plt                   <write@plt>
        fd: 3 (/memfd:oño (deleted))
        buf: 0x555555558060 ◂— 0x10102464c457f
        n: 0x7130

   0x555555555157    mov    rdi, qword ptr [r13]
   0x55555555515b    lea    rdx, [rsp + 8]
   0x555555555160    xor    eax, eax                        EAX => 0
   0x555555555162    mov    rsi, r14
   0x555555555165    mov    qword ptr [rsp + 0x10], r15
   0x55555555516a    add    rdi, 5
   0x55555555516e    and    qword ptr [rsp + 0x18], 0
   0x555555555174    call   __isoc99_sscanf@plt         <__isoc99_sscanf@plt>

   0x555555555179    mov    rcx, qword ptr [rsp + 8]
   0x55555555517e    mov    rdi, r15
───────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp   0x7fffffffd590 ◂— 0x5cff9b68cc2
01:0008│       0x7fffffffd598 —▸ 0x7fffffffd5c8 ◂— 0
02:0010│       0x7fffffffd5a0 ◂— 0x6000000017
03:0018│       0x7fffffffd5a8 ◂— 0
... ↓          4 skipped
─────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► 0   0x555555555152
   1   0x7ffff7dc21ca __libc_start_call_main+122
   2   0x7ffff7dc228b __libc_start_main+139
   3   0x5555555551f1
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> dump binary memory ono_dump_2.bin Quit
pwndbg> dump binary memory ono_dump_2.bin 0x555555558060 0x555555558060+0x7130
```
Mở IDA lên xem thử thui:
![image](https://hackmd.io/_uploads/BypgCDvTkx.png)
Có vẻ như lần này bài đã đụng đến cái `envp`, `*a3 + 5` là nó lấy số kiểu `llu` phía sau `oño=`, để biết số đó là gì thì ta check lại envp của 2 file trước đó.
**File1:**
![image](https://hackmd.io/_uploads/BJ5_kOP6Jl.png)
**File2:**
![image**](https://hackmd.io/_uploads/B1zqk_w61e.png)
Để lấy `envp` của file 1 thì ta debug, còn `envp` của file 2 là `envp của file 1 ^ flag của file 2`.
```python
#!/usr/bin/python3 
from pwn import * 
from z3 import *
from ctypes import * 

chall1_envp = 8940005473485202353
chall2_ans = 3486735211078842207
header = b'\x7fELF\x02\x01\x01\x00' 
ans = BitVec('ans', 64)
solver = Solver()
chall2_envp = chall2_ans ^ chall1_envp
print(hex(chall2_envp))
qword_4048 = 0x7E34413175C77CE8
qword_4050 = 0x0D4308F96CA525D5A
solver.add((ans % 0x539) != 800)
solver.add((qword_4048 + qword_4050 * ans) == chall2_envp)
if solver.check() == sat:
    m = solver.model()
    res = m[ans].as_long()
    print(hex(res))
    print(p64(res))
```
``` linux
~/currentCTF 11m 31s
❯ ./solve.py
0x4c7218afdb1154ee
0x31775f476e314b6f
b'oK1nG_w1'
```
Khúc này debug mình thấy vô IDA dump memory tiện hơn, đặt breakpoint ngay đoạn này sửa giá trị rồi dump là ok:
![image](https://hackmd.io/_uploads/B1Izg_w6Je.png)
Script dump:
```IDA
    auto fp, ea;
    fp = fopen("dumped_3.bin", "wb");
    for (ea = 0x556B2E57E060; ea < 0x556B2E57E060 + 0x38B8; ea++)
        fputc(Byte(ea), fp);
```
Mở IDA ra xem thử thôi lets go, ở đây thì mình đã rev và rename rồi:
``` c++
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v3; // ecx
  int v4; // r9d
  unsigned __int64 v5; // r8
  int v6; // esi
  int v7; // eax
  int v8; // r9d
  void *v9; // r11
  char *v10; // r9
  char v11; // r10
  int v13; // eax
  int v14; // r10d
  __int64 chall3_envp; // [rsp+8h] [rbp-30h] BYREF
  __int64 chall3_flag; // [rsp+10h] [rbp-28h] BYREF
  __int64 v17[4]; // [rsp+18h] [rbp-20h] BYREF

  __isoc99_sscanf(*a3 + 5, "%llu", &chall3_envp);
  __isoc99_sscanf(*a2, "%llu", &chall3_flag);
  v3 = 0;
  v4 = 0;
  v5 = chall3_envp ^ chall3_flag;
  v6 = 0;
  chall3_flag ^= chall3_envp;
  do
  {
    if ( ((v5 >> v3) & 1) == 0 )
    {
      ++v4;
      v6 = v3;
    }
    ++v3;
  }
  while ( v3 != 64 );
  v17[0] = 0LL;
  if ( !v4 || (v7 = dfsBit(v5, v6, v17), v7 == v8) )
  {
    v9 = &unk_4060;
    v10 = (char *)&unk_4060;
    while ( 1 )
    {
      v11 = *v10;
      v17[0] = 0LL;
      if ( v11 )
      {
        if ( ((v5 >> ((unsigned __int8)v10 - (unsigned __int8)v9)) & 1) == 0 )
          break;
        v13 = dfsBit(v5, (int)v10 - (int)v9, v17);
        if ( v13 != v14 )
          break;
      }
      if ( ++v10 == (char *)&unk_4060 + 64 )
      {
        puts(win_message);
        return 0LL;
      }
    }
  }
  puts(s);
  return 1LL;
}
```
``` c++
__int64 __fastcall dfsBit(unsigned __int64 number, int bitPos, _QWORD *a3)
{
  _DWORD *v3; // r12
  unsigned int v5; // ebx
  int v6; // edx
  int v7; // eax

  v3 = &unk_4040;
  *a3 |= 1LL << bitPos;
  v5 = 1;
  do
  {
    v6 = bitPos / 8 + *v3;
    v7 = bitPos % 8 + v3[1];
    if ( (v7 | (unsigned int)v6) <= 7
      && (((unsigned __int8)(*a3 >> (v7 + 8 * (unsigned __int8)v6)) | (unsigned __int8)((number >> bitPos) ^ (number >> (v7 + 8 * (unsigned __int8)v6)))) & 1) == 0 )
    {
      v5 += dfsBit(number, v7 + 8 * v6, a3);
    }
    v3 += 2;
  }
  while ( (_DWORD *)((char *)&unk_4040 + 32) != v3 );
  return v5;
}
```
Tóm tắt flow nó như này:
- Đem flag xor với envp
- Check xem các số 0 có liên thông với nhau khi duyệt **DFS** không (Nếu ở vị trí bit thứ i thì ta có thể duyệt sang bit thứ i - 1, i + 1, i - 8, i + 8 với điều kiện là 2 giá trị của bit bằng nhau).
- Xong rồi sẽ có một điều kiện để check bit thứ i xem có bật hay không, và số bit mà bit đó **DFS** tới thỏa điều kiện.
- Nếu thỏa tất cả điều kiện trên thì là flag.

Mình sẽ nói về cái điều kiện thứ 3 ở trên:
``` linux
[[19, 1], [33, 5], [39, 7], [42, 3], [45, 4], [52, 6], [58, 2]]
```
Nó yêu cầu tại vị trí thứ 19 (bit thứ 19 phải bật) DFS sẽ tới 1 đỉnh, 33 thì 5 đỉnh, và tương tự với các giá trị khác. Xong rồi các bit không bật thì phải liên thông với nhau. Điều thú vị ở đây là nghĩ đi nghĩ lại thì kiểu gì đây cũng là một bài **NP Hard** ???. Mình không giải nhiều bài **NP Hard** lắm nhưng ở đây mình biết được phần cuối flag kiểu gì cũng là `}`.
Đến đây thì mình sẽ không cố gắng giải một bài `NP Hard`, mà mình sẽ ngồi mò nó với những dữ liệu mà mình có (như là phần cuối là `}`, phần đầu là `t`).
Đến đây thì ngồi suy nghĩ các kiểu rồi, mình cũng không nhớ hết được mình đã nghĩ ra cái gì nên đây là script cho part cuối của flag:
``` c++
#include <bits/stdc++.h>
using namespace std;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
int num, goal, cntVis[64], cur;
vector<int> vecVis[10];
bool vis[64], mp[64];
bool check(int x) {
    return x >= 0 && x < 64;
}
void dfs(int u) {
    if (mp[u]) {
        return;
    }
    if (!vis[u]) {
        return;
    }
    num++;
    // cout << u << ' ';
    mp[u] = 1;
    for (int i = 0; i < 4; i++) {
        int v = u + dx[i] * 8 + dy[i];
        if (vis[v] && check(v)) {
            dfs(v);
        }
    }
}
void dfs0(int u) {
    if (mp[u]) {
        return;
    }
    if (vis[u]) {
        return;
    }
    num++;
    // cout << u << ' ';
    mp[u] = 1;
    for (int i = 0; i < 4; i++) {
        int v = u + dx[i] * 8 + dy[i];
        if (!vis[v] && check(v)) {
            dfs0(v);
        }
    }
}
int main() {
    vector<pair<int, int>> dfsList;
    dfsList.push_back({19, 1});
    dfsList.push_back({33, 5});
    dfsList.push_back({39, 7});
    dfsList.push_back({42, 3});
    dfsList.push_back({45, 4});
    dfsList.push_back({52, 6});
    dfsList.push_back({58, 2});
    fill(vis, vis + 64, 0);
    vis[63] = 1;
    vis[62] = 1;
    vis[61] = 1;
    vis[60] = 1;
    vis[55] = 1;
    vis[52] = 1;
    vis[57] = 1;
    vis[58] = 1;

    vis[45] = 1;
    vis[37] = 1;
    vis[29] = 1;
    vis[21] = 1;

    vis[43] = 1;
    vis[42] = 1;
    vis[35] = 1;

    vis[39] = 1;
    vis[31] = 1;
    vis[23] = 1;
    vis[15] = 1;
    vis[7] = 1;
    vis[6] = 1;
    vis[5] = 1;

    vis[33] = 1;    
    vis[25] = 1;
    vis[17] = 1;
    vis[9] = 1;
    vis[10] = 1;

    vis[19] = 1;

    for (auto [i, cnt] : dfsList) {
        num = 0;
        fill(mp, mp + 64, 0);
        dfs(i);
        cout << i << ' ' << num << ' ' << cnt << "\n";
    }
    num = 0;
    fill(mp, mp + 64, 0);
    dfs0(59);
    cout << num << ' ';
    num = 0;
    unsigned long long x = 0;
    for (int i = 0; i < 64; i++) {
        if (vis[i]) {
            num++;
            x |= (1ll << i);
        }
    }
    cout << 64 - num << "\n";
    cout << (x ^ 0x8bb15f9ec5f5ce94);
    return 0;
}
```
Final flag:
```
dice{n0w_w3r3_c0oK1nG_w1tH_g4s!}
```
## misc/bcu-binding
![image](https://hackmd.io/_uploads/BJ2tcJv61e.png)

Tải file về thì ta search thử 'dice{' để tìm flag và nó ra thật @@
Có vẻ như nó được giấu trong phần nền trắng để không thể nhìn thấy...

![Screenshot 2025-03-29 213449](https://hackmd.io/_uploads/HyXwoyPTJx.png)
    
    Flag: dice{r3ad1ng_th4_d0cs_71ccd}
## misc/dicecap
![image](https://hackmd.io/_uploads/SJIsi1wake.png)

Đề cho ta 1 file pcap nên ta mở nó lên bằng wireshark và xem thử:
![Screenshot 2025-03-29 215831](https://hackmd.io/_uploads/HJg7hyDTkx.png)

Thử tìm theo từ khóa thì ta ra được 2 kênh khả nghi chứa flag theo protocol FTP-DATA.
Export file ra thì ta được 1 file main theo định dạng ELF cùng 1 tệp zip cần mật khẩu mà trong đó chứa flag.txt có thể lấy flag.

File main được định dạng ELF nên ta mở IDA để đọc code của nó
![Screenshot 2025-03-29 223714](https://hackmd.io/_uploads/rkmmR1Dpkg.png)

Ở đây file main khởi tạo mật khẩu và nó có thể được dùng để giải nén file zip. Đọc rõ hơn:
1. Lấy thời gian hiện tại và làm tròn theo phút là được s
2. Lấy 5 ký tự đầu tiên của locale (dest)
3. Ghép username (v3)
4. Kết hợp tất cả thành mật khẩu (v6)

Đầu tiên, khi ta xem lại thì gói main nằm ở No.168 và khi tra lại thì ta lấy được thời gian v1 rồi sau đó tính theo công thức để lấy s.
![image](https://hackmd.io/_uploads/HkktlxD61x.png)

```python!
v1 = 1743126530
s = v1 - v1 % 60
# 1743126480
```
Thứ hai về locate thì ta lấy mặc định là `en_US.UTF-8` nhưng chỉ lấy 5 kí tự đầu cho dest là: `en_US`

Cuối cùng, username ta sẽ có được bằng cách tìm kiếm những request được gửi lên thông qua gói tin mà ta có thể xem được và đây là kết quả:
![image](https://hackmd.io/_uploads/r1dCpyvTke.png)
```python=
# solution.py
v1 = 1743126530
s = v1 - v1 % 60
locale_str = "en_US"
username = "hacker"
password = f"{s}{locale_str}{username}"
print(password) #1743126480en_UShacker
```
Giải nén file zip và lấy flag thôi:
![image](https://hackmd.io/_uploads/Bkm0GxDTyl.png)

    Flag: dice{5k1d_y0ur_w@y_t0_v1ct0ry_t0d4y!!!}
## crypto/vorpal-sword
![image](https://hackmd.io/_uploads/SkrisLv6yx.png)
```python=
# server.py
#!/usr/local/bin/python

import secrets
from Crypto.PublicKey import RSA

DEATH_CAUSES = [
    "a fever",
    "dysentery",
    "measles",
    "cholera",
    "typhoid",
    "exhaustion",
    "a snakebite",
    "a broken leg",
    "a broken arm",
    "drowning",
]


def run_ot(key, msg0, msg1):
    """
    https://en.wikipedia.org/wiki/Oblivious_transfer#1–2_oblivious_transfer
    """
    x0 = secrets.randbelow(key.n)
    x1 = secrets.randbelow(key.n)
    print(f"n: {key.n}")
    print(f"e: {key.e}")
    print(f"x0: {x0}")
    print(f"x1: {x1}")
    v = int(input("v: "))
    assert 0 <= v < key.n, "invalid value"
    k0 = pow(v - x0, key.d, key.n)
    k1 = pow(v - x1, key.d, key.n)
    m0 = int.from_bytes(msg0.encode(), "big")
    m1 = int.from_bytes(msg1.encode(), "big")
    c0 = (m0 + k0) % key.n
    c1 = (m1 + k1) % key.n
    print(f"c0: {c0}")
    print(f"c1: {c1}")


if __name__ == "__main__":
    with open("flag.txt") as f:
        flag = f.read().strip()

    print("=== CHOOSE YOUR OWN ADVENTURE: Vorpal Sword Edition ===")
    print("you enter a cave.")

    for _ in range(64):
        print("the tunnel forks ahead. do you take the left or right path?")
        key = RSA.generate(1024)
        msgs = [None, None]
        page = secrets.randbits(32)
        live = f"you continue walking. turn to page {page}."
        die = f"you die of {secrets.choice(DEATH_CAUSES)}."
        msgs = (live, die) if secrets.randbits(1) else (die, live)
        run_ot(key, *msgs)
        page_guess = int(input("turn to page: "))
        if page_guess != page:
            exit()

    print(f"you find a chest containing {flag}")
```

Theo thử thách, bài này nói về [1-2 oblivious transfer](https://en.wikipedia.org/wiki/Oblivious_transfer#1%E2%80%932_oblivious_transfer) nên mình cũng lên mạng tìm hiểu và đây là các bước mà giao thức này thực hiện:
1. Alice có tin nhắn $m_0, m_1$ và 1 cặp khóa RSA $(e, d, N)$. Bob biết được khóa công khai $(e, N)$ của Alice và muốn tin nhắn $m_c$ , với $c \in \{0, 1\}$.
2. Alice tạo ngẫu nhiên 2 số $x_0, x_1$ và gửi cho Bob.
3. Bob tạo ngẫu nhiên 1 số $k$ và gửi $v = b+k^e \mod N$ cho Alice.
4. Alice tính $k_0 = (v-x_0)^d\mod N$ và $k_1 = (v-x_1)^d\mod N$ rồi gửi $m_0'=m_0+k_0\mod N$ và $m_1'=m_1+k_1\mod N$ cho Bob.
5. Bob tính và lấy được $m_c=m_c'-k\mod N$ và không lấy được thông tin gì từ $m_{1-c}$

Đọc file server thì ta thấy là hàm `run_ot` đúng là đang thực hiện giống như 4 bước đầu trong giao thức trên. Và khi ta truy ngược lại thì ta cần biết được `page` mà được giấu trong tin nhắn `live`
```python=
live = f"you continue walking. turn to page {page}."
die = f"you die of {secrets.choice(DEATH_CAUSES)}."
msgs = (live, die) if secrets.randbits(1) else (die, live)
run_ot(key, *msgs)
page_guess = int(input("turn to page: "))
if page_guess != page:
    exit()
```
Tuy nhiên vì `msgs` được random giữa `(live,die)` và `(die, live)`, cùng với việc ta phải nhập đúng `page` trong 64 lần liên tiếp thì rõ ràng việc làm như Bob là lấy được 1 trong 2 tin nhắn không khả thi. Vì xác suất trúng được hết chỉ là $\dfrac{1}{2^{64}}$.
Vì ta đã biết được list của các `DEATH_CAUSES` nên ta cũng có thể tính được hết tất cả các `die` của chall, tức là ta có thể phân biệt được đâu là `live` và đâu là `die`.

Nhận thấy, ta đã biết $c_0, c_1$, 1 trong hai giá trị $m_0, m_1$ thì việc tìm ra được giá trị còn lại phải dựa vào 1 biểu thức tuyến tính giữa 4 biến này xảy ra.
Nói rõ hơn:
$$
c_0\equiv m_0+k_0\mod n
$$

$$
c_1\equiv m_1+k_1\mod n
$$

$$
\Leftrightarrow a c_0 -bc_1\equiv am_0-bm_1 +(ak_0 -bk_1)\mod n \ \ \ \ \ (*)
$$

Có nghĩa là ta phải tìm v làm sao mà $ak_0-bk_1 \equiv 0 \mod n$, với a,b bất kỳ

Điều này tương đương với: $(v-x_0)^d\equiv k(v-x_1)^d \mod n$
$$
\Leftrightarrow (v-x_0)^{de}\equiv k^e(v-x_1)^{de}\mod n
$$

Chọn v có điều kiện $\gcd(v-x_0, n) = 1$ và sử dụng định lý Euler cùng $de\equiv 1\mod \phi(n)$
$$
\Leftrightarrow v-x_0\equiv k^e(v-x_1)\mod n
$$

$$
\Leftrightarrow (k^e - 1)(v-x_1)\equiv x_1-x_0\mod n
$$

Thử $k=1,-1$ đều không thỏa nên ta thử $k = 2$

Lúc này, $k^e-1=2^{65537}-1$ là 1 số Mersenne nên các ước nguyên tố của nó có [tính chất](https://vi.wikipedia.org/wiki/S%E1%BB%91_nguy%C3%AAn_t%E1%BB%91_Mersenne#C%C3%A1c_%C4%91%E1%BB%8Bnh_l%C3%BD_v%E1%BB%81_s%E1%BB%91_nguy%C3%AAn_t%E1%BB%91_Mersenne): Nếu q là một số nguyên tố của 1 số Mersenne: $q\equiv 1\mod 65537, q\equiv \pm1\mod 8$
Mặt khác, $n$ là một số nguyên tố 1024 bit nằm trong khoảng $2^{1023}$ đến $2^{1024}-1$ và là rất lớn so với ước nguyên tố điển hình của $2^{65537}-1$. Hơn nữa, theo [Định lý số nguyên tố](https://vi.wikipedia.org/wiki/%C4%90%E1%BB%8Bnh_l%C3%BD_s%E1%BB%91_nguy%C3%AAn_t%E1%BB%91) thì có thể có khoảng $\dfrac{2^{1023}}{ln(2^{1023})} \approx  \dfrac{2^{1023}}{709}$ số nguyên tố 1024 bit. Do đó với 1 số nguyên tố 1024 bit ngẫu nhiên, gần như chắc chắn là số đó không có khả năng là ước của $2^{65537}-1$

Vì vậy, $\gcd(k^e-1,n)=1$ tương đương với  tồn tại nghịch đảo modulo n của $2^{65537}-1$
$$
\Leftrightarrow v-x_1\equiv (k^e - 1)^{-1}(x_1-x_0)\mod n
$$

$$
\Leftrightarrow v\equiv (k^e - 1)^{-1}(x_1-x_0) + x_1\mod n
$$
```python=
e = 65537
k = 2**e - 1
def sol_v(n, k, x0, x1):
    d = inverse(k, n)
    return (x1 + d*(x1-x0)) % n
```

Bây giờ trở lại với $(*)$ thì ta được biểu thức tuyến tính sau:
$$
\Leftrightarrow c_0 -2c_1\equiv m_0-2m_1\mod n \ \ \ \ \ (**)
$$

Lúc này, ta chia 2 trường hợp rõ ràng:
1.  nếu `page` nằm trong `live` của $m_0$:
$$
(**)\Leftrightarrow m_0\equiv c_0 -2c_1+2m_1\mod n
$$
```python=
def case_m0(n, c0, c1, lst):
    page = -1
    for i in lst:
        k = (c0 - 2*c1 + 2*i) % n
        try:
            res = long_to_bytes(k)
        except ValueError:
            continue
        if res.startswith(b"you continue walking. turn to page "):
            page = res[35:-1]
            if page.isdigit():
                break
    return page
```
2. nếu `page` nằm trong `live` của $m_1$:
$$
(**)\Leftrightarrow m_1\equiv (m_0-c_0+2c_1)\times 2^{-1}\mod n
$$
```python=
def case_m1(n, c0, c1, lst):
    page = -1
    inv = inverse(2, n)
    for j in lst:
        k = (j - c0 + 2*c1)*inv % n
        try:
            res = long_to_bytes(k)
        except ValueError:
            continue
        if res.startswith(b"you continue walking. turn to page "):
            page = res[35:-1]
            if page.isdigit():
                break
    return page
```
Đến đây rồi thì, ta brute-force hết cái list của `die` thì ta sẽ biết được trường hợp nào là thỏa và tìm được `page`. Remote tới server và nhận flag thôi :333
```python=
# solution.py
from pwn import *
from Crypto.Util.number import *

import sys
sys.set_int_max_str_digits(0)


DEATH_CAUSES = [
    'a fever',
    'dysentery',
    'measles',
    'cholera',
    'typhoid',
    'exhaustion',
    'a snakebite',
    'a broken leg',
    'a broken arm',
    'drowning',
]
lst = []
for i in DEATH_CAUSES:
    die = f'you die of {i}.'
    s = int.from_bytes(die.encode(), 'big')
    lst.append(s)

def rev_data():
    return int(io.recvline().strip().decode())

def take_data1():
    io.recvuntil("n: ")
    n = rev_data()
    io.recvuntil("x0: ")
    x0 = rev_data()
    io.recvuntil("x1: ")
    x1 = rev_data()
    return n, x0, x1

e = 65537
k = 2**e - 1
def sol_v(n, k, x0, x1):
    d = inverse(k, n)
    return (x1 + d*(x1-x0)) % n

def take_data2():
    io.recvuntil("c0: ")
    c0 = rev_data()
    io.recvuntil("c1: ")
    c1 = rev_data()
    return c0, c1

def case_m0(n, c0, c1, lst):
    page = -1
    for i in lst:
        k = (c0 - 2*c1 + 2*i) % n
        try:
            res = long_to_bytes(k)
        except ValueError:
            continue
        if res.startswith(b"you continue walking. turn to page "):
            page = res[35:-1]
            if page.isdigit():
                break
    return page

def case_m1(n, c0, c1, lst):
    page = -1
    inv = inverse(2, n)
    for j in lst:
        k = (j - c0 + 2*c1)*inv % n
        try:
            res = long_to_bytes(k)
        except ValueError:
            continue
        if res.startswith(b"you continue walking. turn to page "):
            page = res[35:-1]
            if page.isdigit():
                break
    return page

io = remote("dicec.tf", 31001)

for _ in range(64):
    n, x0, x1 = take_data1()
    v = sol_v(n, k, x0, x1)
    
    io.recvuntil("v: ")
    io.sendline(str(v))
    c0, c1 = take_data2()
    
    page = case_m0(n, c0, c1, lst)
    if page == -1:
        page = case_m1(n, c0, c1, lst)

    io.recvuntil("turn to page: ")
    io.sendline(page)

data = io.recvall()
print(data)
io.close()
```
    Flag: dice{gl3am1ng_g0ld_doubl00n}
    
{%hackmd j1OspBFEQaSWOBxLkcN4xA %}