---
# System prepended metadata

title: 低レイヤ勉強会 1/13

---

# 低レイヤ勉強会 1/13

> 予習でやってもらった部分も一応軽く触れていきます

## 第5章 ROT13 (Malleus P.42)
### はじめに
実行ファイルを開くと
```
Lbh pna fbyir guvf ceboyrz ol sbezng fgevat nggnpx
```
と出てくる(はずですが、ダウンロードしたものはなぜか出てこないです)

これを問題名のROT13で復号すると...
(=アルファベットを13文字ずらす)
```
You can solve this problem by format string attack
```
となる。
この内容より、今回の想定解法は**書式文字列攻撃**。

書式文字列を使って、main 関数のリターンアドレスから libc のアドレスをリークし、GOT を
One-gadget RCE に書き換える。

### 問題のソースコード
```5.c
//  gcc rot13.c -o rot13 -no-pie -fcf-protection=none
#include <stdio.h>
#include <unistd.h>

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

int main()
{
    char buf[0x100] = "";
    int i = 0;

    setup();

    fgets(buf, sizeof buf, stdin);

    for (i=0; i<0x100; i++) {
        int d = 0;
        if ('A'<=buf[i] && buf[i]<='M' ||
            'a'<=buf[i] && buf[i]<='m')
            d = +13;
        if ('N'<=buf[i] && buf[i]<='Z' ||
            'n'<=buf[i] && buf[i]<='z')
            d = -13;
        buf[i] += d;
    }

    printf(buf);
    printf("https://en.wikipedia.org/wiki/ROT13\n");
}
```



## 5.2 書式文字列攻撃
上のソースコードで、
```
33:  printf(buf); 
```
が脆弱性にあたる。
### 値の読み出し
C 言語の関数の呼び出しでは、呼び出された関数は引数の個数を知ることはできない。

x64 の関数の引数は第 1 引数から順に、rdi、rsi、rdx、rcx、r8、r9 レジスタで渡さ
れ、第 7 引数以降はスタックに積まれる。


必要な引数を渡さずに、printf 関数の書式文字列に引数を参照する書式指定子（%）を含めると、printf 関数はレジスタやスタック
の値を出力する。これによって、レジスタとスタックの値を順に読み出せる。

>このあたりの挙動はしっかり理解できていないのですが、関数内で宣言した変数はスタック(第７変数以降)に格納されるようです

また、POSIX の仕様として、%n$lx のように書くとn+1 番目の引数を読み出せる。
POSIXはいわばUNIXの規格のようなもので、UNIX系・派生OS(Linux, macOS)にはこの手法が使えるということ
(Windows, Visual C++ 環境では不可!
もっとも問題サーバでWindowsが動いていることはほぼないが、実機で動かす際は極力仮想マシン・WSLでLinuxを使ったほうが確実)

攻撃側がスタックに文字列を書き込めるならば、文字列を出力する %s を使って、任意の固定のアドレスの値を読み出すこともできる。
%lxは直接引数の値を出力するが、%s は引数の値のアドレスから文字列を読み出す。

**注意:** この形式で読み出す場合はhexdumpコマンドを併用しますが、リトルエンディアンの法則に伴い、deadbeaf:12345678 は 78 56 34 12 文字目 のように対応します。



### 値の書き込み
書式指定子 %n は、%n の直前までに出力した文字数を、対応する引数で指定されたアドレスに書き込む。

abc の 3 文字を出力しているので x には 3 が、追加で 1234 の 4 文字->合計７文字を出力しているの
で y には **7** が書き込まれる。
(fsa_test3.c 参照)



### 新しい用語: Stack Smashing Protection
コンパイラを用いて，**スタックバッファオーバーフロー脆弱性を悪用した攻撃を防ぐ対策技術**．

スタック内のebpレジスタの値格納場所とローカル変数の間に配置した、**canary**と呼ばれる値の書き換えの有無によってスタックバッファオーバーフロー攻撃を検知する．

プログラムの実行中で，関数の呼び出し時にスタックにcanaryを挿入し，関数からリターンする直前に埋め込んだcanaryの値が書き換えられていないかチェックする．
canaryの値が書き換えられていない場合は，通常通り実行が完了するが，スタックバッファオーバーフロー攻撃によってバッファのサイズ以上の入力があり，**canaryの値が書き換えられていた場合は，実行を中止**.

([出典:明治大学情報セキュリティ研究室 バッファオーバーフローへの対策技術入門](https://www.saitolab.org/infra_kaisetsu/)
ここではバッファオーバーフローについても詳しく解説してあるので、まだちょっとあやふやな人は読んでみてください)

> 余談:Canary = カナリアという名前は、坑道に有毒ガスが無いかをチェックするためにカナリアを連れて行き、カナリアが死んだら危険な状態になっていると判断して実行を中止...というのがが元ネタのようです。
> ここでも、カナリアが死亡 = canaryが書き換えられた -> プログラムの実行を中止 という感じです。

### Checksec.sh を使ってSSPの有効/無効を確かめる
一応導入方法を再掲しておきます:
```a.sh
$ cd
$ git clone https://github.com/slimm609/checksec.sh
```

使い方は以下の通りです。
**実行ファイルが存在するディレクトリに移動した後、**--file="" の""内に調べたい実行ファイル名を入れてください。

注意: checksecは、パスを通してないので、checksecと打ち込んでも"checksec: command not found"となります。
```a.sh
$ ~/checksec.sh/checksec --file="rot13"
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified      Fortifiable     FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   70) Symbols       No    0     rot13
```
うまくいけば上記の通りに出力され、SSPについては"STACK CANARY"を見るとわかります。
今回は、**"CANARY FOUND"となっているため、SSPは有効**です。


## 次回は問題を攻略していきます
今回の説明だけではピンとこないと思いますが、攻略の際に今日の知識を使っていくので
頭の片隅に留めておいてください
