# SCIST 11/4 PPC writeup [toc] --- # 前言(廢話) 那天去上了 高中職生資安研習營Reverse 但發現題目跟上上周的crypto差不多 想做個簡單的筆記 自己記錄順便練習寫 還有給不太知道怎麼解題的朋友們 ~~對不起我超不會寫Markdown 想拿這次當錯練習 排版醜請見諒~~ 第一次寫writeup 寫得不是很好 如果有建議修改或錯字請DC : osga_ >< --- # trange 簡單介紹一下這東東 把`for in range()` 改成 `for in trange()` 它就可以從平常我們看不到的東東變成進度條! 讓你知道程式有沒有在運行 知道程式進度 ## 安裝tqdm `pip3 install tqdm` ## 使用trange `from tqdm import trange` 然後將`for in range()` 改成 `for in trange()`就可以ㄌ **Ex:** ```py= from tqdm import trange a = 0 for i in trange(100): a += 1 print(a) ``` **輸出** ``` 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [00:00<00:00, 861253.39it/s] 100 ``` --- # PPC 啟動! **LAB:https://misc.ctf.scist.org/challenges** ``` Timeout : 120s Author : Curious ``` --- ## Welcome **題目:** ```py nc lab.scist.org 33340 ``` **輸出:** ``` ====== WELCOME TO PPC LAB ====== Please wait for a few second, let me calculate the flag Flag : FLAG{自己試!} ``` 就讓你知道一下netcat怎麼連而已 得分:> --- ## Count **題目:** ```py nc lab.scist.org 33342 ``` **輸出:** ``` ====== Welcome To Counting Game ====== You just need to count from 1 to 100 ! ------ wave 1/100 ------ > ``` 簡單來說 要輸入1~100 但每題都有設定timeout ~~手速快就完事~~ 這時候就要派出**PPC**! ```python= from pwn import * from tqdm import trange r = remote('lab.scist.org',33342) r.recvlines(2) for i in trange(100): r.recvline() r.sendline(str(i+1).encode()) r.interactive() ``` 簡單來說就用一個for迴圈讓他跑100次 然後把`i`發出去 - `r.recvlines(2)` 先將前面兩行歡迎訊息消掉 - `r.sendline(str(i+1).encode())` 將`i+1`送出去 或可以把range改成 (1,101) 直接發`i`出去 **記得轉為字串及enocde()才能送出!** --- ## Nth **題目:** ```py nc lab.scist.org 33341 ``` **輸出:** ``` ====== Welcome To Nrd Game ====== Find the nth largest number for me ! ----- Example ----- Numbers : 1 3 6 12 8 9 13 11 Nth : 2 Answer > 12 ----- Now Your Turn ----- Numbers : 95428 43107 68999 44959 20582 17824 43000 96683 25038 23973 23362 34621 98862 30027 86133 17717 26866 3227 29225 84436 95097 62890 75567 45392 17127 81902 27262 99182 54735 66583 65134 83324 87666 37434 13982 45707 17274 55563 39812 73439 35990 82794 84661 85071 72506 63538 62846 99953 70418 64472 26725 53364 48351 34926 56125 26644 19948 31978 89109 35304 60511 55109 54800 57736 3800 49045 70823 23185 92989 93927 42273 50355 48576 62535 82988 65420 51082 85560 28895 84730 35536 6791 14173 12531 13875 7184 42636 59429 50308 23990 69871 8685 51052 17690 62185 9425 53470 77613 28704 66284 48377 37994 46546 89717 87297 44887 82015 41237 48054 76059 56603 94766 4869 32288 68321 93701 2541 13222 75466 7715 65166 48605 44394 50588 94552 74209 46780 28416 70984 81066 72425 73695 31362 81813 49104 95064 76670 49761 61048 83057 96273 9821 62558 97703 76990 40359 83269 3863 75027 30941 12074 25938 91660 3158 43207 16605 52222 51535 95961 74705 8439 83668 32365 63390 40072 1993 34685 23206 40805 39915 70128 11350 73644 77785 8000 27376 83587 49389 20764 1323 41704 32576 51697 80708 87829 38038 72704 19308 63156 11388 296 56756 70586 63462 56867 49515 78094 53699 21056 12857 47734 46613 51640 39222 10166 5710 99132 36805 22430 9215 79182 76165 59305 18700 13938 67191 22425 13664 38436 96147 49653 10515 7300 50332 27892 60710 78711 88650 41476 46225 62001 20666 49113 85240 91655 64350 23649 49444 26458 30891 23895 67532 1461 61053 15950 6326 40562 2574 63964 72577 43511 25301 65019 41247 87421 67745 17104 59866 88814 31518 16247 79843 56847 82709 97827 14598 59647 63078 93020 96605 44381 14832 52957 24217 3856 64657 68657 65581 13633 75298 78294 10929 80146 9297 15169 3476 12440 6924 7614 48965 3729 66338 40565 67507 66144 52905 5561 58970 63464 92685 Nth : 213 Answer > ``` 窩 它給了一大堆數字 上面有演示 **需要你回傳Numbers裡第Nth大的數字** **`演示 : Nth : 2 第二大的數字是12`** ~~你眼睛夠快算術夠快就可以ㄌ~~ 但它每次數字都給不同 所以沒辦法一直nc然後看timeout再nc一次~ **所以 PPC!** 可以先獲取numbers和Nth 再將numbers排好順序比較順位 ```py= from pwn import * r = remote('lab.scist.org',33341) r.recvlines(8) num = r.recvline() num = num[10:].strip().decode().split() num = [int(i) for i in num] num.sort() n = int(r.recvline()[6:].strip().decode()) ans = num[-n] r.sendlineafter(b'> ',str(ans).encode()) r.interactive() ``` - `num = r.recvlines(8)` 一樣先削掉歡迎訊息和範例 - `num = r.recvline()` 獲取numbers / `num = num[10:].strip().decode().split()` 將它整理整理 - `num[10:] ` 將前面的 `Numbers : ` 給消除掉 剩我們要的數值而已 - `strip()` 將後面 `\n` 消掉 - `decode()` 解碼 - `split()` 將每個數儲存成list ``` ['85856', '523', '99314', '32583', '95800', '82955', '19974', '49491', '5118', '51305', '27878', '21893', '94189', '86714', '79216', '79394', '5424', '1681', '35937', '52076', '79973', '74912', '31321', '26545', '20021', '42729', '25169', '56280', '40264', '84129', '37900', '83183', '68466', '72382', '57851', '43811', '34723', '94455', '19608', '48206', '99570', '87868', '1984', '23012', '24377', '1454', '57252', '69512', '42386', '719', '47159', '91677', '97755', '79312', '81371', '6535', '18912', '63111', '52725', '2940', '23169', '52040', '64002', '26311', '84612', '36030', '36344', '44771', '44147', '99660', '25508', '97796', '48136', '98913', '70067', '36348', '77396', '51850', '94941', '10120', '95210', '63609', '5245', '90643', '98418', '67262', '40133', '64292', '43138', '38089', '83646', '21376', '27121', '56003', '67466', '53586', '20160', '33648', '19930', '73915'] ``` 差不多長這樣 - `num = [int(i) for i in num]` 將每個list的數字都轉為整數 - `num.sort()` 將num從小排到大 - `n = int(r.recvline()[6:].strip().decode())` 獲取nth並轉成變數 - `ans = num[-n]` 答案為num裡第n大個 - `r.sendlineafter(b'> ',str(ans).encode())` 因為它最後回傳答案是 `Answer > ` 所以當我們接收到 `> `的時候回傳答案 --- ## Guess **題目:** ```py nc lab.scist.org 33343 ``` **輸出:** ``` ====== Welcome To Guess Game ====== Guess the secret number ! Secret number is between 1 ~ 1073741824 ! Guess > ``` 猜數字時間! 給你超大範圍要你在2分鐘找出數字! 如果比較小它會: ``` Guess > 123 Too small ! ``` 如果比較大它會: ``` Guess > 129308120938190283 Too big ! ``` 我們可以用迴圈一直嘗試數值範圍直到得到答案 有趣的事這題有給Hintㄝ! **Hint: 二分搜尋法** ```py= # range_list[0] <= ans < range_list[1] range_list = [1, 1073741825] while True: guess_num = (range_list[0] + range_list[1]) // 2 if ans < guess_num: range_list[1] = guess_num elif ans > guess_num: range_list[0] = guess_num + 1 else: assert guess_num == ans break ``` 我們只要把數值和回答結果套進去就完事! ```py= from pwn import * r = remote('lab.scist.org',33343) r.recvlines(2) # range_list[0] <= ans < range_list[1] range_list = [1, 1073741825] while True: guess_num = (range_list[0] + range_list[1]) // 2 r.sendlineafter(b'> ',str(guess_num).encode()) ans = r.recvline() if b'Too small !' in ans: range_list[0] = guess_num elif b'Too big !' in ans: range_list[1] = guess_num + 1 else: print(ans) break r.interactive() ``` 小改了一下code 將比較大小那邊改成 **偵測回答為甚麼** 使用二分搜尋法 將範圍縮小並找到答案 - `if b'Too small !' in ans:` 如果`ans`裡面有`Too small !`的話 就將最小值改為發出的值 - `elif b'Too big !'` 跟上面一樣 --- ## Calculator **題目:** ```py nc lab.scist.org 33344 ``` **輸出:** ``` ====== Welcome To The Calculator Game ====== We got some equations here, but the operator is missing. Can you help us? ----- wave 1/100 ----- 95 ? 62 = 33 which operator (+/-/*) ? ``` 這題要我們回答一些簡單的數學! 判斷它給我們的題目是 `加 || 減 || 乘` 並回答100次 我們可以用for跑100次題目 然後判斷 數字為多少 ```py= from pwn import * from tqdm import trange r = remote('lab.scist.org',33344) r.recvlines(3) for i in trange(100): r.recvline() text = r.recvline().strip().split() a,b,c = int(text[0]),int(text[2]),int(text[4]) if (a + b) == c: ans = b'+' elif (a - b) == c: ans = b'-' else: ans = b'*' r.sendlineafter(b'? ',ans) r.interactive() ``` - `text = r.recvline().strip().split()` 獲取它給得算式 並拆分成list - `a,b,c = int(text[0]),int(text[2]),int(text[4])` - 像這題`56 ? 63 = 119`就可以拆分成`[b'56', b'?', b'63', b'=', b'119']` - 將text裡第0個、第2個、第3個 也就是我們需要的數字 處存在a、b、c 並且轉換成整數 - 然後再用if判斷哪個為答案 並存取再ans中 - ` r.sendlineafter(b'? ',ans)` 因為它回答是`which operator (+/-/*) ? ` 所以當我們收到 `? `就回傳ans --- ## Alphabet **題目:** ```py nc lab.scist.org 33345 ``` **輸出:** ``` ====== Welcome To Alphabet Game ====== Count how many English letters there are ! ----- Example ----- Message : xW_JDb9MMS=p7IP4KD1?I:[+KH)4<<JuyY906544"4AE]I Answer > 23 ----- Now Your Turn ----- ------ wave 1/100 ------ Message : 3Dc!?l7zdZ'OjFDqoA77:6=H]v$b2\$Z&# Answer > ``` 它要我們算出Message裡有多少個字是英文字母 ```py= from pwn import * from tqdm import trange r = remote('lab.scist.org',33345) r.recvlines(7) for i in trange(100): r.recvline() mes = r.recvline().strip().decode()[10:] cout = 0 for char in mes: if char.isalpha(): cout+=1 r.sendlineafter(b'> ',str(cout).encode()) r.interactive() ``` - `mes = r.recvline().strip().decode()[10:]`獲取message並將前面`Message : `給消除掉 - 定義一個cout 為 0 判斷英文字母有多少個 - `for char in mes:` 再mes裡跑迴圈 並存在char裡 - `if char.isalpha(): cout+=1`如果char是英文字母 cout就+1 - [isaplha()](https://www.runoob.com/python/att-string-isalpha.html) 判斷是否為英文 - `r.sendlineafter(b'> ',str(cout).encode())` 一樣 收到 `> `就回傳答案 --- ## Digit **題目:** ```py nc lab.scist.org 33346 ``` **輸出:** ``` ====== Welcome To Digit Game ====== Can you help me recognize some digits? ----- wave 1/100 ----- ### # # # # # # # # # # ### What is this digit? ``` 這題非常有趣w 它會出現圖形並請你回答該圖形是甚麼數字 幸好它有提供`server.py` **`server.py:`** ```py= import random, sys from secret import flag digit = [ ''' ### # # # # # # # # # # ### ''', ''' # ## # # # # # ##### ''', ''' ##### # # # ##### # # ####### ''', ''' ##### # # # ##### # # # ##### ''', ''' # # # # # # # ####### # # ''', ''' ####### # # ###### # # # ##### ''', ''' ##### # # # ###### # # # # ##### ''', ''' ####### # # # # # # # ''', ''' ##### # # # # ##### # # # # ##### ''', ''' ##### # # # # ###### # # # ##### ''' ] msg = '''====== Welcome To Digit Game ====== Can you help me recognize some digits? ''' def main(): print(msg) for i in range(100): print(f'----- wave {i + 1}/100 -----') num = random.randint(0, 9) print(digit[num]) if int(input('What is this digit? ')) == num: print('You are right !') else: print('No, Your eye is malfunctioned.') sys.exit() print(f'Flag : {flag}') try: main() except: sys.exit() ``` 把它get下來後 發現它是用陣列處存圖形並呼叫 ```py= from pwn import * from tqdm import trange r = remote('lab.scist.org',33346) digit = [ ''' ### # # # # # # # # # # ### ''', ''' # ## # # # # # ##### ''', ''' ##### # # # ##### # # ####### ''', ''' ##### # # # ##### # # # ##### ''', ''' # # # # # # # ####### # # ''', ''' ####### # # ###### # # # ##### ''', ''' ##### # # # ###### # # # # ##### ''', ''' ####### # # # # # # # ''', ''' ##### # # # # ##### # # # # ##### ''', ''' ##### # # # # ###### # # # ##### ''' ] r.recvlines(3) for i in trange(100): r.recvline() numbres = digit.index(b'\n'.join(r.recvlines(9)).decode()) r.sendlineafter(b'? ',str(numbres).encode()) r.recvline() r.interactive() ``` 所以我們可以把陣列複製下來 並將它回答得結果進行對比 - `numbres = digit.index(b'\n'.join(r.recvlines(9)).decode())`獲取答案並對比 - `r.recvlines(9)`獲取圖形 圖形占了9行(加上下空格) - `b'\n'.join(r.recvlines(9))` 將獲取的圖形 使用`\n(換行符號)`作為分隔圖 將圖形連接成單一個得字元 - `.decode()` 將二進制轉為ASCII - `digit.index()` 將前面處立好的字串回傳給上面的digit陣列 對比該圖形在陣列的位置 - `r.sendlineafter(b'? ',str(numbres).encode())` 回傳答案 --- ## PI **題目:** ```py nc lab.scist.org 33347 ``` **輸出:** ``` ====== Welcome To PI Game ====== Give me PI with certain length ----- Example ----- Length : 5 PI > 3.1416 ----- Now Your Turn ----- ------ wave 1/100 ------ Length = 2468 PI > ``` 它會給你一個長度 需要你回答該長度的PI 然後回答100次! **Ex:** ` Length : 3 PI > 3.14 ` 一樣有提供`server.py`! **`server.py:`** ```py= import random, sys from secret import flag msg = '''====== Welcome To PI Game ====== Give me PI with certain length ----- Example ----- Length : 5 PI > 3.1416 ----- Now Your Turn ----- ''' PI = '3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278865936153381827968230301952035301852968995773622599413891249721775283479131515574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012858361603563707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104752162056966024058038150193511253382430035587640247496473263914199272604269922796782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955321165344987202755960236480665499119881834797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548161361157352552133475741849468438523323907394143334547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383827967976681454100953883786360950680064225125205117392984896084128488626945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645995813390478027590099465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203496252451749399651431429809190659250937221696461515709858387410597885959772975498930161753928468138268683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506016842739452267467678895252138522549954666727823986456596116354886230577456498035593634568174324112515076069479451096596094025228879710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821682998948722658804857564014270477555132379641451523746234364542858444795265867821051141354735739523113427166102135969536231442952484937187110145765403590279934403742007310578539062198387447808478489683321445713868751943506430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675142691239748940907186' def get_PI(length): if int(PI[length + 1]) >= 5: return PI[:2] + str(int(PI[2: length + 1]) + 1).ljust(length - 1, '0') return PI[:length + 1] def main(): print(msg) for i in range(100): print(f'------ wave {i + 1}/100 ------') length = random.randint(2, len(PI) - 2) print(f'Length = {length}') if input('PI > ') != get_PI(length): sys.exit() print(f'Flag : {flag}') try: main() except: sys.exit() ``` 它有給如何獲取該長度的PI函式 那就借用一下:> ```py= from pwn import * from tqdm import trange r = remote('lab.scist.org',33347) r.recvlines(7) PI = '3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278865936153381827968230301952035301852968995773622599413891249721775283479131515574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012858361603563707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104752162056966024058038150193511253382430035587640247496473263914199272604269922796782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955321165344987202755960236480665499119881834797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548161361157352552133475741849468438523323907394143334547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383827967976681454100953883786360950680064225125205117392984896084128488626945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645995813390478027590099465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203496252451749399651431429809190659250937221696461515709858387410597885959772975498930161753928468138268683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506016842739452267467678895252138522549954666727823986456596116354886230577456498035593634568174324112515076069479451096596094025228879710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821682998948722658804857564014270477555132379641451523746234364542858444795265867821051141354735739523113427166102135969536231442952484937187110145765403590279934403742007310578539062198387447808478489683321445713868751943506430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675142691239748940907186' def get_PI(length): if int(PI[length + 1]) >= 5: return PI[:2] + str(int(PI[2: length + 1]) + 1).ljust(length - 1, '0') return PI[:length + 1] for i in trange(100): r.recvline() length = int(r.recvline().decode()[9:]) ans = get_PI(length) r.sendlineafter(b'> ',str(ans).encode()) r.interactive() ``` - `def get_PI(length):` 利用它`server.py`裡的函式 直接獲取該長度會輸出甚麼 - `length = int(r.recvline().decode()[9:])`獲取length的值 - `ans = get_PI(length` 將length丟到get_PI的函式 獲取該長度的值 - `r.sendlineafter(b'> ',str(ans).encode())` 回傳答案! --- ## Hanoi **對不起我不會 好難 我連邏輯都不懂Orz**