# AIS3 pre-exam 2021
## Info
* 參賽ID: Zero871015
* 比賽日期:2021/05/22 ~ 2021/05/24
## ReSident evil villAge [Crypto]
* 本題有給程式碼,是一題關於RSA的題目。
* 題目中給予`n`和`e`,只要成功簽章`Ethan Winters`這段文字就可以得到Flag。
* 然後題目中可以幫你簽章任意內容,但不能是題目要求的`Ethan Winters`。
* 翻了一下我之前的[RSA筆記](https://hackmd.io/@Zero871015/RSA)就知道這題是考`Chosen Ciphertext Attack`。
* 詳細的攻擊理論可以參閱[這篇](https://crypto.stackexchange.com/questions/2323/how-does-a-chosen-plaintext-attack-on-rsa-work)。
### 以下實作
* 先連到server並存下資訊。
```python=
# connect server and get n ane e
r = remote('quiz.ais3.org', 42069)
r.recvline()
n = int(r.recvline().split(b'=')[1].strip())
e = int(r.recvline().split(b'=')[1].strip())
print('n =', n)
print('e =', e)
```
* 將題目要求的名字乘上`2^e`後送出(記得要mod`n` 不然會太大),以迴避掉輸入不能是`Ethan Winters`的限制。
```python=
# name * 2^e to pass the restriction
name = b'Ethan Winters'
name = bytes_to_long(name)
name_b = (name * pow(2, e)) % n
r.sendline('1')
r.recv()
r.sendline(format(name_b, 'x'))
```
* 因為`2^e^d`在mod`n`的情況下會變回`2`,因此回傳的訊息除二就是答案了。
```python=
# the original result is half
sig2 = int(r.recvline().split(b': ')[2].strip())
print(sig2)
r.sendline('2')
r.sendline(str(sig2 // 2))
r.interactive()
```
## Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi [Welcome]
* Welcome的題目,主要只是要熟悉工具和環境。
* 把題目hint給的code執行後,畫面上會出現一個密碼盒。
* 可以輸入`0~9`的數字,如果正確就可以輸入下一位,否則會顯示`LOCKED`,要重新連線才能繼續。
* 因為大概只需要輸入十個數字左右,最差只要嘗試`100`次左右就可以開了,所以可以直接暴力破解。
* 可以手動也可以寫程式自動化,因為不難寫這裡就不附code了。
* 開了密碼盒之後server會丟回flag並斷開連線,如果不是用程式自動抓回傳可能會來不及看到flag畫面就消失,可以使用`python3 | tee output.txt`這種方式記錄下所以內容,事後就可以在`output.txt`看到flag了。
## Microcheese [Misc]
* 這題有給程式碼,執行一下會發現是一個遊戲程式。

* 基本上就是拿石頭遊戲,和AI輪流拿,每次可以選擇一列,拿一顆以上的石頭;拿到最後一顆就贏了。
* 大家對於這種遊戲應該不陌生,都知道有必勝法了,當然用一般的方法是不可能贏的了AI的。
* 原本的解法應該是要利用遊戲中的存檔機制,去破解存檔後給的密文,自己做出一個可贏的存檔。

* 不過這題code寫爛了,稍微看一下就可以發現在`play()`裡面,忘記去處理玩家選擇`0`, `1`, `2`以外的選項。
* 所以我們選擇`3`的話,就會執行上次拿石頭的動作,而如果我們上一次拿的行數已經不存在,就不會拿石頭了。
```python=
if choice == '0':
pile = int(input('which pile do you choose? '))
count = int(input('how many stones do you remove? '))
if not game.make_move(pile, count):
print_error('that is not a valid move!')
continue
elif choice == '1':
game_str = game.save()
digest = hash.hexdigest(game_str.encode())
print('you game has been saved! here is your saved game:')
print(game_str + ':' + digest)
return
elif choice == '2':
break
# no move -> player wins!
if game.ended():
win = True
break
else:
print_move('you', count, pile)
game.show()
```
* 利用這點,我們就可以讓對手一直拿石頭,直到我們可以拿最後一顆的時候再出手,就可以得到flag了。
## 🐰 Peekora 🥒 [Reverse]

* 因為這題太油了所以決定要做這題。
* 題目給了一個pkl檔和一段提示的command,先執行看看。

* 執行後要求我們輸入flag,隨便亂打則是什麼都沒發生,推測這是一支驗證flag的程式。
* google了一下pkl這個副檔名之後,會發現這是python的pickle序列化檔案。
* 使用`cat`可以直接看到opcode,或是使用python的pickletools可以得到好看一點的解譯內容。
```python=
import pickletools
data = open("flag_checker.pkl", "rb")
data = data.read()
pickletools.dis(data)
```
```
0: c GLOBAL '__builtin__ input'
19: ( MARK
20: S STRING 'FLAG: '
30: t TUPLE (MARK at 19)
31: R REDUCE
32: p PUT 0
35: 0 POP
36: c GLOBAL '__builtin__ getattr'
57: p PUT 1
60: 0 POP
61: g GET 1
64: ( MARK
65: ( MARK
66: c GLOBAL '__builtin__ exit'
84: c GLOBAL '__builtin__ str'
101: l LIST (MARK at 65)
102: S STRING '__getitem__'
117: t TUPLE (MARK at 64)
118: R REDUCE
119: p PUT 2
122: 0 POP
123: g GET 2
126: ( MARK
127: g GET 1
130: ( MARK
131: g GET 0
134: S STRING 'startswith'
148: t TUPLE (MARK at 130)
149: R REDUCE
150: ( MARK
151: S STRING 'AIS3{'
160: t TUPLE (MARK at 150)
161: R REDUCE
162: t TUPLE (MARK at 126)
163: R REDUCE
164: ( MARK
165: t TUPLE (MARK at 164)
166: R REDUCE
167: g GET 2
170: ( MARK
171: g GET 1
174: ( MARK
175: g GET 0
178: S STRING 'endswith'
190: t TUPLE (MARK at 174)
191: R REDUCE
192: ( MARK
193: S STRING '}'
198: t TUPLE (MARK at 192)
199: R REDUCE
200: t TUPLE (MARK at 170)
201: R REDUCE
202: ( MARK
203: t TUPLE (MARK at 202)
204: R REDUCE
205: g GET 2
208: ( MARK
209: g GET 1
212: ( MARK
213: g GET 1
216: ( MARK
217: g GET 0
220: S STRING '__getitem__'
235: t TUPLE (MARK at 216)
236: R REDUCE
237: ( MARK
238: I INT 6
241: t TUPLE (MARK at 237)
242: R REDUCE
243: S STRING '__eq__'
253: t TUPLE (MARK at 212)
254: R REDUCE
255: ( MARK
256: V UNICODE 'A'
259: t TUPLE (MARK at 255)
260: R REDUCE
261: t TUPLE (MARK at 208)
262: R REDUCE
263: ( MARK
264: t TUPLE (MARK at 263)
265: R REDUCE
266: g GET 2
269: ( MARK
270: g GET 1
273: ( MARK
274: g GET 1
277: ( MARK
278: g GET 0
281: S STRING '__getitem__'
296: t TUPLE (MARK at 277)
297: R REDUCE
298: ( MARK
299: I INT 9
302: t TUPLE (MARK at 298)
303: R REDUCE
304: S STRING '__eq__'
314: t TUPLE (MARK at 273)
315: R REDUCE
316: ( MARK
317: V UNICODE 'j'
320: t TUPLE (MARK at 316)
321: R REDUCE
322: t TUPLE (MARK at 269)
323: R REDUCE
324: ( MARK
325: t TUPLE (MARK at 324)
326: R REDUCE
327: g GET 1
330: ( MARK
331: g GET 0
334: S STRING '__getitem__'
349: t TUPLE (MARK at 330)
350: R REDUCE
351: ( MARK
352: I INT 9
355: t TUPLE (MARK at 351)
356: R REDUCE
357: p PUT 3
360: 0 POP
361: g GET 2
364: ( MARK
365: g GET 1
368: ( MARK
369: g GET 1
372: ( MARK
373: g GET 0
376: S STRING '__getitem__'
391: t TUPLE (MARK at 372)
392: R REDUCE
393: ( MARK
394: I INT 11
398: t TUPLE (MARK at 393)
399: R REDUCE
400: S STRING '__eq__'
410: t TUPLE (MARK at 368)
411: R REDUCE
412: ( MARK
413: V UNICODE 'p'
416: t TUPLE (MARK at 412)
417: R REDUCE
418: t TUPLE (MARK at 364)
419: R REDUCE
420: ( MARK
421: t TUPLE (MARK at 420)
422: R REDUCE
423: g GET 2
426: ( MARK
427: g GET 1
430: ( MARK
431: g GET 1
434: ( MARK
435: g GET 0
438: S STRING '__getitem__'
453: t TUPLE (MARK at 434)
454: R REDUCE
455: ( MARK
456: I INT 14
460: t TUPLE (MARK at 455)
461: R REDUCE
462: S STRING '__eq__'
472: t TUPLE (MARK at 430)
473: R REDUCE
474: ( MARK
475: g GET 3
478: t TUPLE (MARK at 474)
479: R REDUCE
480: t TUPLE (MARK at 426)
481: R REDUCE
482: ( MARK
483: t TUPLE (MARK at 482)
484: R REDUCE
485: g GET 1
488: ( MARK
489: g GET 0
492: S STRING '__getitem__'
507: t TUPLE (MARK at 488)
508: R REDUCE
509: ( MARK
510: I INT 1
513: t TUPLE (MARK at 509)
514: R REDUCE
515: p PUT 4
518: 0 POP
519: g GET 2
522: ( MARK
523: g GET 1
526: ( MARK
527: g GET 1
530: ( MARK
531: g GET 0
534: S STRING '__getitem__'
549: t TUPLE (MARK at 530)
550: R REDUCE
551: ( MARK
552: I INT 5
555: t TUPLE (MARK at 551)
556: R REDUCE
557: S STRING '__eq__'
567: t TUPLE (MARK at 526)
568: R REDUCE
569: ( MARK
570: V UNICODE 'd'
573: t TUPLE (MARK at 569)
574: R REDUCE
575: t TUPLE (MARK at 522)
576: R REDUCE
577: ( MARK
578: t TUPLE (MARK at 577)
579: R REDUCE
580: g GET 2
583: ( MARK
584: g GET 1
587: ( MARK
588: g GET 1
591: ( MARK
592: g GET 0
595: S STRING '__getitem__'
610: t TUPLE (MARK at 591)
611: R REDUCE
612: ( MARK
613: I INT 10
617: t TUPLE (MARK at 612)
618: R REDUCE
619: S STRING '__eq__'
629: t TUPLE (MARK at 587)
630: R REDUCE
631: ( MARK
632: V UNICODE 'z'
635: t TUPLE (MARK at 631)
636: R REDUCE
637: t TUPLE (MARK at 583)
638: R REDUCE
639: ( MARK
640: t TUPLE (MARK at 639)
641: R REDUCE
642: g GET 2
645: ( MARK
646: g GET 1
649: ( MARK
650: g GET 1
653: ( MARK
654: g GET 0
657: S STRING '__getitem__'
672: t TUPLE (MARK at 653)
673: R REDUCE
674: ( MARK
675: I INT 12
679: t TUPLE (MARK at 674)
680: R REDUCE
681: S STRING '__eq__'
691: t TUPLE (MARK at 649)
692: R REDUCE
693: ( MARK
694: V UNICODE 'h'
697: t TUPLE (MARK at 693)
698: R REDUCE
699: t TUPLE (MARK at 645)
700: R REDUCE
701: ( MARK
702: t TUPLE (MARK at 701)
703: R REDUCE
704: g GET 2
707: ( MARK
708: g GET 1
711: ( MARK
712: g GET 4
715: S STRING '__eq__'
725: t TUPLE (MARK at 711)
726: R REDUCE
727: ( MARK
728: g GET 1
731: ( MARK
732: g GET 0
735: S STRING '__getitem__'
750: t TUPLE (MARK at 731)
751: R REDUCE
752: ( MARK
753: I INT 13
757: t TUPLE (MARK at 752)
758: R REDUCE
759: t TUPLE (MARK at 727)
760: R REDUCE
761: t TUPLE (MARK at 707)
762: R REDUCE
763: ( MARK
764: t TUPLE (MARK at 763)
765: R REDUCE
766: g GET 2
769: ( MARK
770: g GET 1
773: ( MARK
774: g GET 1
777: ( MARK
778: g GET 0
781: S STRING '__getitem__'
796: t TUPLE (MARK at 777)
797: R REDUCE
798: ( MARK
799: I INT 8
802: t TUPLE (MARK at 798)
803: R REDUCE
804: S STRING '__eq__'
814: t TUPLE (MARK at 773)
815: R REDUCE
816: ( MARK
817: V UNICODE 'w'
820: t TUPLE (MARK at 816)
821: R REDUCE
822: t TUPLE (MARK at 769)
823: R REDUCE
824: ( MARK
825: t TUPLE (MARK at 824)
826: R REDUCE
827: g GET 2
830: ( MARK
831: g GET 1
834: ( MARK
835: g GET 1
838: ( MARK
839: g GET 0
842: S STRING '__getitem__'
857: t TUPLE (MARK at 838)
858: R REDUCE
859: ( MARK
860: I INT 7
863: t TUPLE (MARK at 859)
864: R REDUCE
865: S STRING '__eq__'
875: t TUPLE (MARK at 834)
876: R REDUCE
877: ( MARK
878: V UNICODE 'm'
881: t TUPLE (MARK at 877)
882: R REDUCE
883: t TUPLE (MARK at 830)
884: R REDUCE
885: ( MARK
886: t TUPLE (MARK at 885)
887: R REDUCE
888: c GLOBAL '__builtin__ print'
907: ( MARK
908: S STRING 'Correct!'
920: t TUPLE (MARK at 907)
921: R REDUCE
922: . STOP
highest protocol among opcodes = 0
```
* 接著就是慢慢讀了,只要知道基本的運作概念其實不難,因為裡面用到的功能很少。
* 基本上就是用stack和memo記錄所有東西。
* 大致上翻譯一下:
* 126~201: 必須是`AIS3{`開頭、`}`結尾
* 238~256: `input[6] == 'A'`
* 299~317: `input[9] == 'j'`
* 352~357 + 456~475: `input[9] == input[14]`
* 394~413: `input[11] == 'p'`
* 510~515 + 712~753: `input[1] == input[13]`
* 552~570: `input[5] == 'd'`
* 613~632: `input[10] == 'z'`
* 675~694: `input[12] == 'h'`
* 799~817: `input[8] == 'w'`
* 860~878: `input[7] == 'm'`
* 統整起來得到`AIS3{dAmwjzphIj}`。
## ⲩⲉⲧ ⲁⲛⲟⲧⲏⲉꞅ 𝓵ⲟ𝓰ⲓⲛ ⲣⲁ𝓰ⲉ [Web]
* 總之有個登入頁面和原始碼頁面。
```!
from flask import Flask, request, make_response, redirect, session, render_template, send_file
import os
import json
app = Flask(__name__)
app.secret_key = os.urandom(32)
FLAG = os.environ.get('FLAG', 'AIS3{TEST_FLAG}')
users_db = {
'guest': 'guest',
'admin': os.environ.get('PASSWORD', 'S3CR3T_P455W0RD')
}
@app.route("/")
def index():
def valid_user(user):
return users_db.get(user['username']) == user['password']
if 'user_data' not in session:
return render_template("login.html", message="Login Please :D")
user = json.loads(session['user_data'])
if valid_user(user):
if user['showflag'] == True and user['username'] != 'guest':
return FLAG
else:
return render_template("welcome.html", username=user['username'])
return render_template("login.html", message="Verify Failed :(")
@app.route("/login", methods=['POST'])
def login():
data = '{"showflag": false, "username": "%s", "password": "%s"}' % (
request.form["username"], request.form['password']
)
session['user_data'] = data
return redirect("/")
@app.route("/logout")
def logout():
session.clear()
return redirect("/")
@app.route("/sauce")
def sauce():
return send_file(__file__, mimetype="text/plain")
if __name__ == '__main__':
app.run(threaded=True, debug=True)
```
* 原始碼頁面可以知道:
1. 有兩組帳密{guest: guest, admin: 不明}
2. 在登入時,會把輸入的username和password做驗證,然後把帳密和`showflag = false`壓成session存起來。
3. 如果存在session、`username!='guest'`、`showflag == true`就會給你flag。
* 可以發現`login()`的地方可以做注入攻擊,只要把`password`裡面加上雙引號就會壞掉。
* 做個簡單的payload測試一下能不能複寫。
* username輸入`123`
* password輸入`guest","password":"guest","username":"guest`
* 會發現可以成功登入guest的頁面,代表注入成功。
* 
* 這樣就可以解決`showflag`的部分,只需要延長上面的payload就可以了:`guest","password":"guest","showflag":true,"username":"guest`
* 接下來要解決`username!='guest'`的部分,我們仔細看看驗證的部分:
* `users_db.get(user['username']) == user['password']`
* 如果`username`根本沒get到東西,`password`會怎麼樣呢...應該會變成空的。
* 因此我們只要username隨便打,password送`null`進去就好了!
* 最後payload:
* `guest","password":null,"showflag":true,"username":"peko`
* 
## COLORS [Reverse]

* 進來這個頁面可以說幾乎啥都沒有,因此我們點開F12開始挖東西。
* 挖一挖,挖到一份很可疑的js檔案,因為這題是逆向所以被搞得有點亂。
```javascript=!
const _0x3eb4 = ['repeat', '1YqKovX', 'NDBCMjBnMzBpNTFKNjA2MDFcMzB3NDAxMzBBNDFqNDBcNDExMzBnNzB1MzBpMTBrMzBsNDA3NjB4NTBpNTBYMTBLMTBJNDBoNTBYMDBLNDFpNTFsNzA2NzBmNDBvMTA2NTA1NzBLMTFuNTE4NzA3NDFCNTAtMTE4NDB3MzFhMTByNDF6NzBLMzA9MjA9MTA9', 'substr', 'output', 'getElementsByTagName', '65022JgPEZp', 'keydown', 'length', 'innerHTML', '677PRUQAU', 'ArrowLeft', 'QWxTM3tCYXNFNjRfaTUrYjByTkluZ35cUXdvLy14SDhXekNqN3ZGRDJleVZrdHFPTDFHaEtZdWZtWmRKcFg5fQ==', '133781JKLWBV', 'ArrowUp', '90407czXCgh', 'PGRpdiBzdHlsZT0id2lkdGg6IDM1MHB4OyBwb3NpdGlvbjogYWJzb2x1dGU7IGJvdHRvbTogMHB4OyBsZWZ0OiAwcHg7Ij48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7IGFuaW1hdGlvbjogcmFpbmJvdyAycyBsaW5lYXIgMHMgaW5maW5pdGUgbm9ybWFsOyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogLTEwcHg7IGxlZnQ6IDUwJTsgZm9udC1zaXplOiAyMHB4OyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoLTUwJSk7IHdpZHRoOiAzNTBweDsiPkhlcmUgaXMgeW91cjxicj4iZW5jb2RlZCIgZmxhZyw8YnI+aW5wdXQgdG8gZW5jb2RlIHNvbWV0aGluZyBlbHNlITwvZGl2PiA8c3ZnIGlkPSLwn5CIIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGlkPSJib2R5Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJmaWxsIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgI2ZmOGQ4YjsgI2ZlZDY4OTsgIzg4ZmY4OTsgIzg3ZmZmZjsgIzhiYjVmZTsgI2Q3OGNmZjsgI2ZmOGNmZjsgI2ZmNjhmNzsgI2ZlNmNiNzsgI2ZmNjk2ODsgI2ZmOGQ4YiAiPjwvYW5pbWF0ZT48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJkIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgTTY3LjEsMTA5LjVjLTkuNiwwLTIzLjYtOC44LTIzLjYtMjRjMC0xMi4xLDE3LjgtNDEsMzcuNS00MWMxNS42LDAsMjMuMywxMC42LDI1LjEsMjAuNSBjMS45LDEwLjcsMy45LDguMiwzLjksMTkuNWMwLDguMi0zLjgsMTctMy44LDIyLjNjMCwzLjksMS40LDcuNCwyLjksMTAuNWMxLjcsMy41LDIuNCw2LjYsMi40LDkuMkg5LjVjMC0xMy43LDEwLjgtMTQsMjEuMS0yMyBjNS42LTQuOSwxMS0xMi40LDE0LjUtMjY7IE01Ni4xLDEwNy41Yy05LjYsMC0yNC42LTEzLjgtMjQuNi0yOWMwLTE2LjIsMTMtNDIsMzMuNS00MmMxNy44LDAsMjIuMywxMS42LDI2LjEsMjIuNSBjMy42LDEwLjMsOS45LDkuMiw5LjksMjAuNWMwLDguMi0xLjgsNy0xLjgsMTIuM2MwLDQuNSwzLjQsOC4yLDYuNCwxNC4xYzIuNSw0LjgsNC44LDExLjIsNC44LDIwLjZoLTk5YzAtMTIuMSw3LjItMTcuNiwxNC43LTI0LjMgYzMuMi0yLjksNi41LTUuOSw5LjItOS44OyBNNDUuMSwxMDkuNWMtNS41LTAuMi0yNy42LTguNC0yNy42LTI3YzAtMTcuOSwxNC44LTQyLDMyLjUtNDJjMTUuNCwwLDI0LDEwLjQsMjYuMSwyMS41IGMxLjMsNi43LDkuOSw5LjgsOS45LDIxLjVjMCw4LjItMC44LDYtMC44LDExLjNjMCw3LjcsMTIuOCw5LDIwLjgsMTVjNy43LDUuOCwxNS41LDE2LjcsMTUuNSwxNi43aC0xMTBjMC00LjgsMS43LTExLjMsNS0xNiBjMy4yLTQuNSw0LjUtOC4zLDUtMTU7IE0zNiwxMjBjLTUuNS0wLjItMjguNS0xMS45LTI4LjUtMzAuNWMwLTE2LjIsMTIuNS00MiwzMy00MmMxNy44LDAsMjEuOCw5LjYsMjUuNiwyMC41IEM2OS43LDc4LjMsNzYsNzguMiw3Niw4OS41YzAsOC4yLTAuOCw0LTAuOCw5LjNjMCw1LjksMTYuNSw3LjgsMjguNCwxNS45YzgsNS41LDE3LjksMTEuOCwxNy45LDExLjhoLTExMGMwLTIuMS0xLjItNS4yLTEuOS0xNC41IGMtMC4zLTMuNi0wLjUtOC4xLTAuNS0xMy45OyBNMzcsMTE5LjVjLTE1LDAuMS0zMy41LTEyLjctMzMuNS0zMEMzLjUsNzMuMywxNiw0NywzNi41LDQ3YzE3LjgsMCwyMi44LDExLDI2LDIyIEM2NS42LDc5LjQsNzMsNzkuMiw3Myw5MC41YzAsNC0xLjgsNi42LTEuOCw4LjNjMCw1LjksMTQuMiw2LjQsMjYuNCwxNS45YzcuNyw2LDEzLjksMTEuOCwxMy45LDExLjhINy41Yy0xLjItMy40LTEuOC03LjMtMS45LTExLjIgYy0wLjItNS4xLDAuMy0xMC4xLDAuOS0xMy43OyBNNDAuNSwxMjEuNWMtMTIuNiwwLTMwLTEzLjQtMzAtMjlDMTAuNSw3Ni4zLDIzLDUzLDQzLjUsNTNjMTQuNSwwLDIyLjgsOS42LDI1LDIyIGMxLjIsNi45LDEwLDkuMiwxMCwyMC41YzAsNCwwLDUuNiwwLDcuM2MwLDQuOSw2LjEsNy41LDExLjIsMTEuOWM1LjgsNSw3LjIsMTEuOCw3LjIsMTEuOEg4LjVjMC0xLjUtMC42LTYuMSwwLjQtMTEuOCBjMC42LTMuNSwxLjktNy41LDQuMy0xMS42OyBNNDguNSwxMjEuNWMtMTIuNiwwLTI1LTYuMy0yNS0xOGMwLTE2LjIsMTMuNy00NywzNi00N2MxNS42LDAsMjQuOCw5LjEsMjcsMjEuNSBjMS4yLDYuOSw3LDkuMiw3LDE4LjVjMCw5LjUtNCwxMS00LDIyYzAsNC4xLDAuNSw1LDEsNmMwLjUsMS4yLDEsMiwxLDJoLTgxYzAtNS4zLDMuMS04LjMsNi4zLTExLjVjMi42LTIuNiw1LjQtNS4zLDYuNy05LjU7IE02OC41LDEyMS41Yy0xMi42LDAtMzMtNS44LTMzLTIzYzAtOS4yLDExLjgtMzYsMzctMzZjMTUuNiwwLDI1LjgsOC4xLDI4LDIwLjUgYzEuMiw2LjksNCw2LjIsNCwxNS41YzAsOS41LTUsMTUuMS01LDIxYzAsMS45LDEsMi4zLDEsNWMwLDEuMiwwLDIsMCwyaC05MWMwLjUtNy42LDcuMS0xMS4xLDEzLjctMTUuN2M0LjktMy40LDkuOS03LjUsMTIuMy0xNC4zOyBNNzMuNSwxMTcuNWMtMTIuNiwwLTMwLTYuMi0zMC0yNWMwLTE0LjIsMjAuOS0zNywzOC0zN2MxNy42LDAsMjUuOCwxMS4xLDI4LDIzLjUgYzEuMiw2LjksMyw3LjIsMywxNi41YzAsMTIuMS02LDE2LjEtNiwyMmMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMUg3LjVjMi4xLTkuNCwxMC40LTEzLjMsMTkuMi0xOS40IGM3LjEtNSwxNC40LTExLjUsMTguOC0yMy42OyBNODAuNSwxMTUuNWMtMTIuNiwwLTMyLTkuMi0zMi0yOGMwLTE0LjIsMjIuOS0zNSw0MC0zNWMxNy42LDAsMjUuOCwxMi4xLDI4LDI0LjUgYzEuMiw2LjksMyw2LjIsMywxNS41YzAsMTIuMS02LDE5LjEtNiwyNWMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMWgtMTAyYzIuMy04LjcsMTEuNi0xMS43LDIwLjgtMjAuMSBjNS4zLTQuOCwxMC41LTExLjQsMTQuMi0yMS45OyBNNjcuMSwxMDkuNWMtOS42LDAtMjMuNi04LjgtMjMuNi0yNGMwLTEyLjEsMTcuOC00MSwzNy41LTQxYzE1LjYsMCwyMy4zLDEwLjYsMjUuMSwyMC41IGMxLjksMTAuNywzLjksOC4yLDMuOSwxOS41YzAsOC4yLTMuOCwxNy0zLjgsMjIuM2MwLDMuOSwxLjQsNy40LDIuOSwxMC41YzEuNywzLjUsMi40LDYuNiwyLjQsOS4ySDkuNWMwLTEzLjcsMTAuOC0xNCwyMS4xLTIzIGM1LjYtNC45LDExLTEyLjQsMTQuNS0yNiAiPjwvYW5pbWF0ZT48L3BhdGg+PHBhdGggaWQ9ImJlYWsiIGZpbGw9IiM3YjhjNjgiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImQiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IiBNNzguMjksNzBjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzc4LjI5LDg1LjUsNzguMjksNzBaOyBNNjIuMjksNjRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzYyLjI5LDc5LjUsNjIuMjksNjRaOyBNNDguMjksNjdjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQ4LjI5LDgyLjUsNDguMjksNjdaOyBNMzYuMjksNzNjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM2LjI5LDg4LjUsMzYuMjksNzNaOyBNMzUuMjksNzVjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM1LjI5LDkwLjUsMzUuMjksNzVaOyBNNDEuMjksODFjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQxLjI5LDk2LjUsNDEuMjksODFaOyBNNTkuMjksODRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzU5LjI5LDk5LjUsNTkuMjksODRaOyBNNzIuMjksODljMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzcyLjI5LDEwNC41LDcyLjI5LDg5WjsgTTgwLjI5LDgyYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4MC4yOSw5Ny41LDgwLjI5LDgyWjsgTTg3LjI5LDc4YzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4Ny4yOSw5My41LDg3LjI5LDc4WjsgTTc4LjI5LDcwYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M3OC4yOSw4NS41LDc4LjI5LDcwWiAiPjwvYW5pbWF0ZT48L3BhdGg+PGVsbGlwc2UgaWQ9ImV5ZS1yaWdodCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iMTAwOzg0OzcwOzU4OzU3OzYzOzgxOzk0OzEwMjsxMDk7MTAwIj48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjxlbGxpcHNlIGlkPSJleWUtbGVmdCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iNjcuNTs1MS41OzM3LjU7MjUuNTsyNC41OzMwLjU7NDguNTs2MS41OzY5LjU7NzYuNTs2Ny41Ij48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjwvc3ZnPjwvZGl2Pg==', '131837PcDnWL', '19pQimXL', '623605MIswVM', 'charCodeAt', 'join', '4WsUYDr', '686oWrfyq', 'body', 'map', 'getElementById', 'textContent', 'match', 'key', '302349wKdZHP', '4OYJFlQ', 'input', 'padStart', 'Backspace'];
function _0x4ebd(_0x532d69, _0x212ed3) {
_0x532d69 = _0x532d69 - 0x1c6;
let _0x3eb4a7 = _0x3eb4[_0x532d69];
return _0x3eb4a7;
}
(function(_0x496f79, _0x226742) {
const _0x463eac = _0x4ebd;
while (!![]) {
try {
const _0x12c745 = -parseInt(_0x463eac(0x1e6)) + parseInt(_0x463eac(0x1ce)) * -parseInt(_0x463eac(0x1de)) + -parseInt(_0x463eac(0x1db)) + -parseInt(_0x463eac(0x1e7)) * parseInt(_0x463eac(0x1d7)) + -parseInt(_0x463eac(0x1d5)) * parseInt(_0x463eac(0x1c9)) + parseInt(_0x463eac(0x1df)) * -parseInt(_0x463eac(0x1d2)) + parseInt(_0x463eac(0x1d9)) * parseInt(_0x463eac(0x1da));
if (_0x12c745 === _0x226742)
break;
else
_0x496f79['push'](_0x496f79['shift']());
} catch (_0xe36f7) {
_0x496f79['push'](_0x496f79['shift']());
}
}
}(_0x3eb4, 0x57a76),
(()=>{
const _0x1cd51f = _0x4ebd
, _0x54579e = _0x1cd51f(0x1d8)
, _0x78ed5a = _0x1cd51f(0x1ca)
, _0x24fcac = _0x1cd51f(0x1d4)
, _0x2a3765 = 0x8
, _0x317b6e = 0xa;
let _0x1e21d9, _0x179193 = 0x0;
function _0xce93(_0x1b497a) {
const _0x9fe181 = _0x1cd51f;
if (!_0x1b497a[_0x9fe181(0x1d0)])
return '';
let _0x4d62de = ''
, _0x23f867 = ''
, _0x5395cb = 0x0;
for (let _0x6e40b4 = 0x0; _0x6e40b4 < _0x1b497a[_0x9fe181(0x1d0)]; _0x6e40b4++)
_0x4d62de += _0x1b497a[_0x9fe181(0x1dc)](_0x6e40b4)['toString'](0x2)[_0x9fe181(0x1c6)](0x8, '0');
_0x5395cb = _0x4d62de[_0x9fe181(0x1d0)] % _0x317b6e / 0x2 - 0x1;
if (_0x5395cb != -0x1)
_0x4d62de += '0'[_0x9fe181(0x1c8)](_0x317b6e - _0x4d62de[_0x9fe181(0x1d0)] % _0x317b6e);
_0x4d62de = _0x4d62de[_0x9fe181(0x1e4)](/(.{1,10})/g);
for (let _0x13c6bb of _0x4d62de) {
let _0x192141 = parseInt(_0x13c6bb, 0x2);
_0x23f867 += _0x9f530c(_0x192141 >> 0x6 & 0x7, _0x192141 >> 0x9, atob(_0x24fcac)[_0x192141 & 0x3f]);
}
for (; _0x5395cb > 0x0; _0x5395cb--) {
_0x23f867 += _0x9f530c(_0x5395cb % _0x2a3765, 0x0, '=');
}
return _0x23f867;
}
let _0x9f530c = (_0xcc032b,_0x2a8089,_0x244c3a)=>'<span><div\x20class=\x22c' + _0xcc032b + '\x20r' + _0x2a8089 + '\x22>' + _0x244c3a + '</div></span>'
, _0x1fdafa = _0x29e3ab=>document[_0x1cd51f(0x1e2)](_0x1cd51f(0x1cc))['innerHTML'] = _0xce93(_0x29e3ab);
document['addEventListener'](_0x1cd51f(0x1cf), _0x23e75c=>{
const _0x12b963 = _0x1cd51f;
if (_0x23e75c[_0x12b963(0x1e5)] === _0x12b963(0x1c7) && _0x179193 == 0xa)
_0x1e21d9[_0x12b963(0x1e3)] = _0x1e21d9['textContent'][_0x12b963(0x1cb)](0x0, _0x1e21d9['textContent'][_0x12b963(0x1d0)] - 0x1);
else {
if (_0x23e75c['key'] === _0x12b963(0x1d6) && !(_0x179193 >> 0x1))
return _0x179193 += 0x1;
else {
if (_0x23e75c[_0x12b963(0x1e5)] === 'ArrowDown' && !(_0x179193 >> 0x2))
return _0x179193 += 0x1;
else {
if (_0x23e75c[_0x12b963(0x1e5)] === _0x12b963(0x1d3) && (_0x179193 == 0x4 || _0x179193 == 0x6))
return _0x179193 += 0x1;
else {
if (_0x23e75c[_0x12b963(0x1e5)] === 'ArrowRight' && (_0x179193 == 0x5 || _0x179193 == 0x7))
return _0x179193 += 0x1;
else {
if (_0x23e75c[_0x12b963(0x1e5)] === 'b' && _0x179193 == 0x8)
return _0x179193 += 0x1;
else {
if (_0x23e75c['key'] === 'a' && _0x179193 == 0x9)
return document[_0x12b963(0x1cd)](_0x12b963(0x1e0))[0x0]['innerHTML'] += atob(_0x54579e),
_0x1e21d9 = document[_0x12b963(0x1e2)](_0x12b963(0x1e8)),
_0x1e21d9[_0x12b963(0x1d1)] = '',
document[_0x12b963(0x1e2)]('output')[_0x12b963(0x1d1)] = atob(_0x78ed5a)[_0x12b963(0x1e4)](/(.{1,3})/g)[_0x12b963(0x1e1)](_0x5efa9e=>_0x9f530c(_0x5efa9e[0x0], _0x5efa9e[0x1], _0x5efa9e[0x2]))[_0x12b963(0x1dd)](''),
_0x179193 += 0x1;
else {
if (_0x23e75c[_0x12b963(0x1e5)][_0x12b963(0x1d0)] == 0x1 && _0x179193 == 0xa)
_0x1e21d9[_0x12b963(0x1e3)] += String['fromCharCode'](_0x23e75c[_0x12b963(0x1e5)][_0x12b963(0x1dc)]());
else
return;
}
}
}
}
}
}
}
_0x1fdafa(_0x1e21d9[_0x12b963(0x1e3)]);
}
);
}
)());
```
* 這邊我稍微通靈了一下,看到檔案裏面有`ArrowDown`、`ArrowRight`、`b`和`a`,又叫我們輸入SecretCode,就想到要打`上上下下左右左右ba`,頁面就變成一個加密器。
* 
* 畫面上一開始的文字是Flag加密成為的結果,然後我們可以輸入隨意文字,他會吐出密文。
* 
* 知道用途是加密之後就可以開始做逆向了,先找出哪一段是加密,然後看懂加密邏輯。
* 可以發現密文後面有很多`=`,估計是padding用的,因此推測加密應該是以下這段:
```javascript=!
function _0xce93(_0x1b497a) {
const _0x9fe181 = _0x1cd51f;
if (!_0x1b497a[_0x9fe181(0x1d0)])
return '';
let _0x4d62de = ''
, _0x23f867 = ''
, _0x5395cb = 0x0;
for (let _0x6e40b4 = 0x0; _0x6e40b4 < _0x1b497a[_0x9fe181(0x1d0)]; _0x6e40b4++)
_0x4d62de += _0x1b497a[_0x9fe181(0x1dc)](_0x6e40b4)['toString'](0x2)[_0x9fe181(0x1c6)](0x8, '0');
_0x5395cb = _0x4d62de[_0x9fe181(0x1d0)] % _0x317b6e / 0x2 - 0x1;
if (_0x5395cb != -0x1)
_0x4d62de += '0'[_0x9fe181(0x1c8)](_0x317b6e - _0x4d62de[_0x9fe181(0x1d0)] % _0x317b6e);
_0x4d62de = _0x4d62de[_0x9fe181(0x1e4)](/(.{1,10})/g);
for (let _0x13c6bb of _0x4d62de) {
let _0x192141 = parseInt(_0x13c6bb, 0x2);
_0x23f867 += _0x9f530c(_0x192141 >> 0x6 & 0x7, _0x192141 >> 0x9, atob(_0x24fcac)[_0x192141 & 0x3f]);
}
for (; _0x5395cb > 0x0; _0x5395cb--) {
_0x23f867 += _0x9f530c(_0x5395cb % _0x2a3765, 0x0, '=');
}
return _0x23f867;
}
```
* 經過我翻譯過後大概變成這樣:
```javascript=!
const _0x1cd51f = _0x4ebd
, _0x54579e = _0x1cd51f(0x1d8)
, _0x78ed5a = _0x1cd51f(0x1ca)
, _0x24fcac = _0x1cd51f(0x1d4)
, 8 = 0x8
, 10 = 0xa;
let _0x1e21d9, _0x179193 = 0x0;
function _0xce93(input) {
const _0x9fe181 = _0x1cd51f;
if (!input[len])
return '';
let bits = ''
, html = ''
, cb = 0x0;
for (let i = 0x0; i < input[len]; i++)
bits += input[charCodeAt](i)['toString'](0x2)[padStart](0x8, '0');
cb = bits[len] % 10 / 0x2 - 0x1;
if (cb != -0x1)
bits += '0'[repeat](10 - bits[len] % 10);
bits = bits[match](/(.{1,10})/g); //每10個切一段
for (let bit of bits) {
let num = parseInt(bit, 0x2);
html += htmlFunction(num >> 0x6 & 0x7, num >> 0x9, "AlS3{BasE64_i5+b0rNIng~\Qwo/-xH8WzCj7vFD2eyVktqOL1GhKYufmZdJpX9}"[num & 0x3f]);
}
for (; cb > 0x0; cb--) {
html += htmlFunction(cb % _0x2a3765, 0, '=');
}
return html;
}
```
* 看code的一些方法:
* chrome可以按F12下中斷點,然後隨便輸入一個字讓它執行加密就會暫停。
* 可以逐步執行之外,某些看不懂的字串代換直接反白會直接幫你翻譯。
* 
* 加密流程大概是:
1. 每個字元用`charCodeAt`轉成二進位數字,不滿八位元的就在前面補`0`。
2. 全部二進位數字串起來,改為每十個切一組,不滿十個的在後面補`0`。
3. 每組數字前四個bit變成顏色的資訊,後六個bit根據密碼表作代換。
* 基本上沒有任何地方是不可逆的加密,所以就寫一個解密器,把加密流程反過來就好了:
```python=!
code = "BgiJ6\\w1Aj\\1guikl7xiXKIhXKil6fo65Kn87B-8warzK"
table = "AlS3{BasE64_i5+b0rNIng~\\Qwo/-xH8WzCj7vFD2eyVktqOL1GhKYufmZdJpX9}"
bits = ""
# get color information
temp = '<div id="output"><span><div class="c4 r0">B</div></span><span><div class="c2 r0">g</div></span><span><div class="c3 r0">i</div></span><span><div class="c5 r1">J</div></span><span><div class="c6 r0">6</div></span><span><div class="c0 r1">\</div></span><span><div class="c3 r0">w</div></span><span><div class="c4 r0">1</div></span><span><div class="c3 r0">A</div></span><span><div class="c4 r1">j</div></span><span><div class="c4 r0">\</div></span><span><div class="c4 r1">1</div></span><span><div class="c3 r0">g</div></span><span><div class="c7 r0">u</div></span><span><div class="c3 r0">i</div></span><span><div class="c1 r0">k</div></span><span><div class="c3 r0">l</div></span><span><div class="c4 r0">7</div></span><span><div class="c6 r0">x</div></span><span><div class="c5 r0">i</div></span><span><div class="c5 r0">X</div></span><span><div class="c1 r0">K</div></span><span><div class="c1 r0">I</div></span><span><div class="c4 r0">h</div></span><span><div class="c5 r0">X</div></span><span><div class="c0 r0">K</div></span><span><div class="c4 r1">i</div></span><span><div class="c5 r1">l</div></span><span><div class="c7 r0">6</div></span><span><div class="c7 r0">f</div></span><span><div class="c4 r0">o</div></span><span><div class="c1 r0">6</div></span><span><div class="c5 r0">5</div></span><span><div class="c7 r0">K</div></span><span><div class="c1 r1">n</div></span><span><div class="c5 r1">8</div></span><span><div class="c7 r0">7</div></span><span><div class="c4 r1">B</div></span><span><div class="c5 r0">-</div></span><span><div class="c1 r1">8</div></span><span><div class="c4 r0">w</div></span><span><div class="c3 r1">a</div></span><span><div class="c1 r0">r</div></span><span><div class="c4 r1">z</div></span><span><div class="c7 r0">K</div></span><span><div class="c3 r0">=</div></span><span><div class="c2 r0">=</div></span><span><div class="c1 r0">=</div></span></div>'
temp = temp.split("class")[1:]
xxx = [int(t[3]) for t in temp]
x = [int(t[6]) for t in temp]
# reverse to original code
for i, c in enumerate(code):
num = table.find(c)
bits += f"{x[i]:01b}{xxx[i]:03b}{num:06b}"
# 10 bits to 8 bits
chunks, chunk_size = len(bits), 8
byte = [ bits[i:i+chunk_size] for i in range(0, chunks, chunk_size) ]
# output flag
for b in byte:
print(chr(int(b, 2)), end="")
```
```
AIS3{base1024_15_c0l0RFuL_GAM3_CL3Ar_thIS_IS_y0Ur_FlaG!}
```
* 成功得到flag。
###### tags: `CTF`