# picoCTF 2019 Writeup
The CTF challenges writeup for picoCTF 2019.
# General Skills
## 2Warm <span style="color: green;"> [Easy] </span>
### Challenge description
Can you convert the number 42 (base 10) to binary (base 2)?
### Solution
用線上工具或在 command line 使用 `bc` 都能將十進制數字轉成二進制:
```bash
$ echo "obase=2; 42" | bc
```
### Resource
[Decimal to Binary converter](https://www.rapidtables.com/convert/number/decimal-to-binary.html?x=44)
### Summary
Flag: `picoCTF{101010}`
## First Grep <span style="color: green;"> [Easy] </span>
### Challenge description
Can you find the flag in [file](https://jupiter.challenges.picoctf.org/static/495d43ee4a2b9f345a4307d053b4d88d/file)? This would be really tedious to look through manually, something tells me there is a better way.
### Solution
將檔案下載下來後,用 grep 搜索帶有 pico 的字串就能找到 flag 了。
```bash
$ cat file | grep pico
picoCTF{grep_is_good_to_find_things_xxxxxxxx}
```
### Summary
Flag: `picoCTF{grep_is_good_to_find_things_xxxxxxxx}`
## Bases <span style="color: green;"> [Easy] </span>
### Challenge description
What does this `bDNhcm5fdGgzX3IwcDM1` mean? I think it has something to do with bases.
### Solution
題目給的字串看起來像是用 base64 編碼過,所以我們可以用 `base64` 指令將其轉換回去:
```bash
$ echo bDNhcm5fdGgzX3IwcDM1 | base64 -d
```
### Summary
Flag: `picoCTF{l3arn_th3_rop35}`
## Warmed Up <span style="color: green;"> [Easy] </span>
### Challenge description
What is 0x3D (base 16) in decimal (base 10)?
### Solution
跟前面 2Warm 那題一樣,可以上網找轉換網站或用 `bc` 解決:
```bash
$ echo "ibase=16; 3D" | bc
```
### Resource
[Hexadecimal to Decimal converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html)
### Summary
Flag: `picoCTF{61}`
## strings it <span style="color: green;"> [Easy] </span>
### Challenge description
Can you find the flag in [file](https://jupiter.challenges.picoctf.org/static/5bd86036f013ac3b9c958499adf3e2e2/strings) without running it?
### Solution
`strings` 能將非文字檔裡的可列印字元找出來,我們可以用這個指令搭配 `grep` 在題目提供的可執行檔中搜尋 flag。
```bash
$ strings strings | grep pico
```
### Summary
Flag: `picoCTF{5tRIng5_1T_xxxxxxxx}`
## what's a net cat? <span style="color: green;"> [Easy] </span>
### Challenge description
Using netcat (nc) is going to be pretty important. Can you connect to `jupiter.challenges.picoctf.org` at port `64287` to get the flag?
### Solution
從題目的描述我們可以知道需要用 `nc` 連上題目提供的 server:
```bash
$ nc jupiter.challenges.picoctf.org 64287
```
連上後就會看到 server 回傳的 flag 了。
### Summary
Flag: `picoCTF{nEtCat_Mast3ry_xxxxxxxx}`
## Lets Warm Up <span style="color: green;"> [Easy] </span>
### Challenge description
If I told you a word started with 0x70 in hexadecimal, what would it start with in ASCII?
### Solution
找線上網站或使用 `xxd` 就能對 hex 進行轉換:
```bash
$ echo 0x70 | xxd -p -r
```
### Resource
[Hex to ASCII Text String Converter](https://www.rapidtables.com/convert/number/hex-to-ascii.html)
### Summary
Flag: `picoCTF{p}`
## mus1c <span style="color: orange;"> [Medium] </span>
### Challenge description
I wrote you a [song](https://jupiter.challenges.picoctf.org/static/c594d8d915de0129d92b4c41e25a2313/lyrics.txt). Put it in the picoCTF{} flag format.
### Solution
查看提示和題目提供的檔案並搜尋後,可以得出檔案內容為用一種叫做 rockstar 的程式語言編寫的。它的官網提供讓我們執行程式的地方:

將檔案中的程式丟上去執行後會拿到一串數字,看起來像是 ascii 對應的數字,因此我們可以用 python 將其轉換回去來拿到 flag:
```python
flag_list = [114, 114, 114, 111, 99, 107, 110, 114, 110, 48, 49, 49, 51, 114]
flag = "".join([chr(x) for x in flag_list])
print(flag)
```
### Resource
[Rockstar: Try it](https://codewithrockstar.com/online)
### Summary
Flag: `picoCTF{rrrocknrn0113r}`
## 1_wanna_b3_a_r0ck5tar <span style="color: orange;"> [Medium] </span>
### Challenge description
> The Rockstar language has changed since this problem was released! Use this Wayback Machine URL to use an older version of Rockstar, [here](https://web.archive.org/web/20190522020843/https://codewithrockstar.com/online).
I wrote you another [song](https://jupiter.challenges.picoctf.org/static/b99c57e4274172bf3c93534b6d59632d/lyrics.txt). Put the flag in the picoCTF{} flag format
### Solution
跟上題一樣,這題也是跟 rockstar 有關的題目,而打開檔案後可以看到程式中看起來有幾個邏輯判斷的地方:
```
A guitar is a six-string
Tommy's been down
Music is a billboard-burning razzmatazz!
Listen to the music
If the music is a guitar
Say "Keep on rocking!"
```
這邊的程式會將 `A guitar` 宣告為 10 (six-string 長度為 10),接收使用者輸入並存放到 `music`,然後比對 `music` 是否等於 `A guitar`。如果這邊不相等的話程式就結束了,因此我們需要通過這個條件判斷式:
```
If 1
Say "Keep on rocking!"
```
我們可以直接將條件判斷式的條件改為 true 來跳過驗證,接著程式就會到達下面的另一個判斷:
```
Listen to the rhythm
If the rhythm without Music is nothing
Tommy is rockin guitar
Shout Tommy!
...
Else Whisper "That ain't it, Chief"
Break it down
```
這裡會將 `rhythm` 與 `Music` 相減去比對是否等於 0,但我們一樣可以直接將判斷式的條件改為 1 去通過,接著執行程式就能跟上一題一樣拿到一串 flag 的 ascii 數字:
```python
flag_list = [66, 79, 78, 74, 79, 86, 73]
flag = "".join([chr(x) for x in flag_list])
print(flag)
```
用一樣的方式轉換回去就能拿到 flag 了。
### Summary
Flag: `picoCTF{BONJOVI}`
## flag_shop <span style="color: orange;"> [Medium] </span>
### Challenge description
There's a flag shop selling stuff, can you buy a flag? [Source](https://jupiter.challenges.picoctf.org/static/64e724ad327f83ad833d9c6baa072b1f/store.c). Connect with `nc jupiter.challenges.picoctf.org 4906`.
### Solution
連接上 server 之後能看到:
```
Welcome to the flag exchange
We sell flags
1. Check Account Balance
2. Buy Flags
3. Exit
Enter a menu selection
```
而在購買 flag 的部分,有兩種選項:
```
Currently for sale
1. Defintely not the flag Flag
2. 1337 Flag
```
接著查看提供的原始碼後我們可以知道,選項 `2. 1337 Flag` 可以拿到我們要的 flag,但是必須要有 100000 才能購買,而我們一開始只有 1100:
```C
int account_balance = 1100;
```
```C
else if(auction_choice == 2){
printf("1337 flags cost 100000 dollars, and we only have 1 in stock\n");
printf("Enter 1 to buy one");
int bid = 0;
fflush(stdin);
scanf("%d", &bid);
if(bid == 1){
if(account_balance > 100000){
FILE *f = fopen("flag.txt", "r");
if(f == NULL){
printf("flag not found: please run this on the server\n");
exit(0);
}
char buf[64];
fgets(buf, 63, f);
printf("YOUR FLAG IS: %s\n", buf);
}
else{
printf("\nNot enough funds for transaction\n\n\n");
}}
}
```
這邊的程式看起來沒有甚麼比較顯眼的突破點,我們可以去看看購買另一種 flag 的部分:
```C
if(auction_choice == 1){
printf("These knockoff Flags cost 900 each, enter desired quantity\n");
int number_flags = 0;
fflush(stdin);
scanf("%d", &number_flags);
if(number_flags > 0){
int total_cost = 0;
total_cost = 900*number_flags;
printf("\nThe final cost is: %d\n", total_cost);
if(total_cost <= account_balance){
account_balance = account_balance - total_cost;
printf("\nYour current balance after transaction: %d\n\n", account_balance);
}
else{
printf("Not enough funds to complete purchase\n");
}
}
}
```
這邊允許我們輸入任意正數來購買對應數量的 flag,然後將輸入的數量乘以金額 900 後存放在變數 `total_cost` 裡,此時如果我們擁有的金錢 `account_balance` 大於購買金額 `total_cost`,就會將我們的錢減掉購買金額重新更新我們的餘額。
因為 `total_cost` 宣告的類型 `int` 的範圍是從 2147483647 ~ -2147483648,在有符號的情況下,如果將 2147483647 + 1 就會造成 overflow 並且變成負數,因此我們可以透過這個方式去控制 `total_cost` 讓它變成負數來增加我們的帳戶金額。
簡單計算一下:
```
2386095 * 900 -> -2147481796 (overflow)
1100 - (-2147481796) -> 2147482896
```
因此我們只要連上 server,選擇 `Buy Flags > Defintely not the flag Flag` 輸入 2386095 就可以拿到大於 100000 的金額,接著再去 `Buy Flags > 1337 Flag` 就能拿到我們要的 flag 了。
### Summary
Flag: `picoCTF{m0n3y_bag5_xxxxxxxx}`
## plumbing <span style="color: orange;"> [Medium] </span>
### Challenge description
Sometimes you need to handle process data outside of a file. Can you find a way to keep the output from this program and search for the flag? Connect to `jupiter.challenges.picoctf.org 7480`.
### Solution
使用 `nc` 連上 server 之後可以看到 server 回傳了很多的訊息,而從題目的 Hint 可以得知這題考的是 piping,所以我們可以用 pipe 把從 `nc` 得到的資料丟給 `grep` 處理:
```bash
$ nc jupiter.challenges.picoctf.org 7480 | grep pico
```
然後就可以從 server 回傳的內容中找到 flag 了。
### Summary
Flag: `picoCTF{digital_plumb3r_xxxxxxxx}`
## Based <span style="color: orange;"> [Medium] </span>
### Challenge description
To get truly 1337, you must understand different data encodings, such as hexadecimal or binary. Can you get the flag from this program to prove you are on the way to becoming 1337? Connect with `nc jupiter.challenges.picoctf.org 29221`.
### Solution
連上 server 後會發現 server 會給不同進制的數字,而我們需要將它轉回 ascii 文字然後送回去。我們可以透過網站或是 python 來幫我們轉換,而下面是 python 轉換的示例:
```python
chr(int(number_in_string, base))
# chr(int("01100110", 2))
# chr(int("141", 8))
# chr(int("74", 16))
```
```python
number_str = [] # put the numbers provided by server here
print("".join([chr(int(x, base)) for x in number_str]))
```
用這個方法就能將題目提供的數字轉回 ascii 單字,而連續答完三題就能拿到 flag 了。
### Summary
Flag: `picoCTF{learning_about_converting_values_xxxxxxxx}`
# Web Exploitation
## dont-use-client-side <span style="color: green;"> [Easy] </span>
### Challenge description
Can you break into this super secure portal? `https://jupiter.challenges.picoctf.org/problem/37821/` ([link](https://jupiter.challenges.picoctf.org/problem/37821/)) or http://jupiter.challenges.picoctf.org:37821
### Solution
進入網頁後,會看到一個要求輸入有效的憑證的表單:

而在網頁上點擊右鍵選擇 inspector (檢查) 並查看原始碼後會看到,它的驗證方式就寫在前端的 `<script>` 裡:
```html
<script type="text/javascript">
function verify() {
checkpass = document.getElementById("pass").value;
split = 4;
if (checkpass.substring(0, split) == 'pico') {
if (checkpass.substring(split*6, split*7) == 'a3c8') {
if (checkpass.substring(split, split*2) == 'CTF{') {
if (checkpass.substring(split*4, split*5) == 'ts_p') {
if (checkpass.substring(split*3, split*4) == 'lien') {
if (checkpass.substring(split*5, split*6) == 'lz_1') {
if (checkpass.substring(split*2, split*3) == 'no_c') {
if (checkpass.substring(split*7, split*8) == '9}') {
alert("Password Verified")
}
}
}
}
}
}
}
}
else {
alert("Incorrect password");
}
}
</script>
```
看起來網站要求的 credential 就是我們的 flag,因此我們只要按照 substring 的順序把 flag 的片段組在一起就能拿到 flag 了。
### Summary
Flag: `picoCTF{no_clients_plz_xxxxxx}`
## logon <span style="color: green;"> [Easy] </span>
### Challenge description
The factory is hiding things from all of its users. Can you login as Joe and find what they've been looking at? `https://jupiter.challenges.picoctf.org/problem/13594/` ([link](https://jupiter.challenges.picoctf.org/problem/13594/)) or http://jupiter.challenges.picoctf.org:13594
### Solution
進入網站後,可以看到一個登入介面:

題目讓我們以 Joe 的身分登入,而我們並沒有 Joe 的密碼。
但如果我們輸入隨便一組使用者名稱和密碼,會發現可以成功登入,從題目的提示中可以知道,這個網站不會驗證除了 Joe 以外其他 user 的密碼,所以我們可以嘗試用其他的 user 來找突破點。
先以隨便一組帳號密碼 (username: a, password: a) 登入,接著在觀察的過程中會發現 cookie 的欄位中有幾個看起來比較特別的:
| Name | Value |
| -------- | -------- |
| username | a |
| password | a |
| admin | False |
網站把身分驗證完之後的資訊存放在了 cookie,看起來可以改動它們來變更我們的身分。
而嘗試後會發現將 `admin` 改為 True 並重整網頁之後就能看到 flag 了:

### Summary
Flag: `picoCTF{th3_c0nsp1r4cy_l1v3s_xxxxxxxx}`
## Insp3ct0r <span style="color: green;"> [Easy] </span>
### Challenge description
Kishor Balan tipped us off that the following code may need inspection: `https://jupiter.challenges.picoctf.org/problem/41511/` ([link](https://jupiter.challenges.picoctf.org/problem/41511/)) or http://jupiter.challenges.picoctf.org:41511
### Solution
題目的名稱提示了 inspector 似乎可以幫助我們解題,我們可以對網站點擊右鍵選擇檢查來打開 inspector,然後切換到 Sources 分頁以查看網頁原始碼。
可以看到在 `problem/41511` 底下有三個檔案,而打開第一個 `41511/` 之後會發現檔案裡面的註解有第一部份的 flag:
```html
<!-- Html is neat. Anyways have 1/3 of the flag: picoCTF{tru3_d3 -->
```
接著查看 `mycss.css`:
```css
/* You need CSS to make pretty pages. Here's part 2/3 of the flag: t3ct1ve_0r_ju5t */
```
還差一個部分的 flag,所以打開 `myjs.js` 查看相關內容:
```javascript
/* Javascript sure is neat. Anyways part 3/3 of the flag: _lucky?xxxxxxxx} */
```
將得到的 flag 片段組合在一起就是我們要的 flag 了。
### Summary
Flag: `picoCTF{tru3_d3t3ct1ve_0r_ju5t_lucky?xxxxxxxx}`
## where are the robots <span style="color: green;"> [Easy] </span>
### Challenge description
Can you find the robots? `https://jupiter.challenges.picoctf.org/problem/36474/` ([link](https://jupiter.challenges.picoctf.org/problem/36474/)) or http://jupiter.challenges.picoctf.org:36474
### Solution
打開網站後,可以看到它詢問我們 `Where are the robots?`,應該是讓我們去查看 `robots.txt`,因此我們可以前往對應的連結查看相關資訊:
```
https://jupiter.challenges.picoctf.org/problem/36474/robots.txt
```
網站回應:
```
User-agent: *
Disallow: /477ce.html
```
`robots.txt` 會定義允許/不允許 search engine crawlers 訪問的網站,有時候我們可以透過 `robots.txt` 來知道網站試圖隱藏哪些檔案,來找到敏感資訊。
可以看到,有一個被禁止的檔案 `/477ce.html`,而如果我們嘗試訪問對應的頁面,就會發現 flag 就藏在這裡。
```
https://jupiter.challenges.picoctf.org/problem/36474/477ce.html
```
### Resource
[維基百科 - robots.txt](https://zh.wikipedia.org/zh-tw/Robots.txt)
### Summary
Flag: `picoCTF{ca1cu1at1ng_Mach1n3s_xxxxx}`
## Irish-Name-Repo 1 <span style="color: orange;"> [Medium] </span>
### Challenge description
There is a website running at `https://jupiter.challenges.picoctf.org/problem/33850/` ([link](https://jupiter.challenges.picoctf.org/problem/33850/)) or http://jupiter.challenges.picoctf.org:33850. Do you think you can log us in? Try to see if you can login!
### Solution
打開網站後,會發現首頁放了好幾個人的照片與他們對應的資訊,似乎沒什麼特別的。
而左上角有一個 menu icon,點擊後可以發現這個網站還有 Support 和 Admin Login 兩個頁面能夠訪問。題目的描述有提到讓我們嘗試登入,因此 Admin Login 應該是我們的突破點:

此時打開 inspector 檢查的話可以看到一個隱藏起來的 debug 欄位:
```html
<input type="hidden" name="debug" value="0">
```
將 debug 的 value 改為 1 並且使用隨便一組帳號密碼登入後,會發現我們可以看到背後使用的 SQL 語句:
```
username: 1
password: 1
SQL query: SELECT * FROM users WHERE name='1' AND password='1'
```
因此我們可以嘗試進行 SQL injection:
```
username: admin' --
password: 1
SQL query: SELECT * FROM users WHERE name='admin' -- ' AND password='1'
```
登入成功後就可以看到 flag 了。
### Summary
Flag: ` picoCTF{s0m3_SQL_xxxxxxxx}`
## Irish-Name-Repo 2 <span style="color: orange;"> [Medium] </span>
### Challenge description
There is a website running at `https://jupiter.challenges.picoctf.org/problem/64649/` ([link](https://jupiter.challenges.picoctf.org/problem/64649/)). Someone has bypassed the login before, and now it's being strengthened. Try to see if you can still login! or http://jupiter.challenges.picoctf.org:64649
### Solution
從題目跟題目的提示可以知道,這題一樣是要嘗試能不能用 SQL injection 登入網站,並且這次在 password field 多了 filter,會檢查 password 的輸入。
因此我們一樣可以跟上一題用同樣的方法,因為我們的 payload 是針對 username field 的:
```
username: admin' --
password: 1
SQL query: SELECT * FROM users WHERE name='admin' -- ' AND password='1'
```
接著登入後就能看到 flag 了。
### Summary
Flag: `picoCTF{m0R3_SQL_plz_xxxxxxxx}`
## Irish-Name-Repo 3 <span style="color: orange;"> [Medium] </span>
### Challenge description
There is a website running at `https://jupiter.challenges.picoctf.org/problem/64649/` ([link](https://jupiter.challenges.picoctf.org/problem/64649/)). Someone has bypassed the login before, and now it's being strengthened. Try to see if you can still login! or http://jupiter.challenges.picoctf.org:64649
### Solution
打開 Admin Login 的頁面後,可以看到這次只剩下 password 的欄位,並且嘗試登入會發現我們的輸入似乎會被進行 rot13 加密:
```
password: abc
SQL query: SELECT * FROM admin where password = 'nop'
```
因此如果我們要讓語句成立,我們可以輸入:
```
password: ' be 1=1 --
SQL query: SELECT * FROM admin where password = '' or 1=1 -- '
```
然後就可以拿到 flag 了。
### Summary
Flag: `picoCTF{3v3n_m0r3_SQL_xxxxxxxx}`
## Client-side-again <span style="color: orange;"> [Medium] </span>
### Challenge description
Can you break into this super secure portal? `https://jupiter.challenges.picoctf.org/problem/56816/` ([link](https://jupiter.challenges.picoctf.org/problem/56816/)) or http://jupiter.challenges.picoctf.org:56816
### Solution
跟前面的 dont-use-client-side 那題一樣,一進入網頁之後可以看到一個登入頁面:

打開原始碼來檢查後會發現一些比較特別的地方,看起來是經過混淆:
```html
<script type="text/javascript">
var _0x5a46 = ['37115}', '_again_3', 'this', 'Password\x20Verified', 'Incorrect\x20password', 'getElementById', 'value', 'substring', 'picoCTF{', 'not_this'];
(function(_0x4bd822, _0x2bd6f7) {
var _0xb4bdb3 = function(_0x1d68f6) {
while (--_0x1d68f6) {
_0x4bd822['push'](_0x4bd822['shift']());
}
};
_0xb4bdb3(++_0x2bd6f7);
}(_0x5a46, 0x1b3));
var _0x4b5b = function(_0x2d8f05, _0x4b81bb) {
_0x2d8f05 = _0x2d8f05 - 0x0;
var _0x4d74cb = _0x5a46[_0x2d8f05];
return _0x4d74cb;
};
function verify() {
checkpass = document[_0x4b5b('0x0')]('pass')[_0x4b5b('0x1')];
split = 0x4;
if (checkpass[_0x4b5b('0x2')](0x0, split * 0x2) == _0x4b5b('0x3')) {
if (checkpass[_0x4b5b('0x2')](0x7, 0x9) == '{n') {
if (checkpass[_0x4b5b('0x2')](split * 0x2, split * 0x2 * 0x2) == _0x4b5b('0x4')) {
if (checkpass[_0x4b5b('0x2')](0x3, 0x6) == 'oCT') {
if (checkpass[_0x4b5b('0x2')](split * 0x3 * 0x2, split * 0x4 * 0x2) == _0x4b5b('0x5')) {
if (checkpass['substring'](0x6, 0xb) == 'F{not') {
if (checkpass[_0x4b5b('0x2')](split * 0x2 * 0x2, split * 0x3 * 0x2) == _0x4b5b('0x6')) {
if (checkpass[_0x4b5b('0x2')](0xc, 0x10) == _0x4b5b('0x7')) {
alert(_0x4b5b('0x8'));
}
}
}
}
}
}
}
} else {
alert(_0x4b5b('0x9'));
}
}
</script>
```
可以看到受到混淆的 `verify()` 是用來驗證 credentials 的函式,因此我們只要知道 `verify()` 的驗證邏輯就能藉此拿到 flag 的資訊了:
```html
<form action="index.html" method="post">
<input type="password" id="pass" size="8"/>
<br/>
<input type="submit" value="verify" onclick="verify(); return false;"/>
</form>
```
查看程式後可以知道,`verify()` 首先會宣告一個叫做 `checkpass` 的變數,看起來像是我們在 `password` 欄位的輸入,接著用變數 `split` 取出對應 indices 的元素,並與 `_0x4b5b()` 函式的回傳值做比較:
```javascript
function verify() {
checkpass = document[_0x4b5b('0x0')]('pass')[_0x4b5b('0x1')];
split = 0x4;
if (checkpass[_0x4b5b('0x2')](0x0, split * 0x2) == _0x4b5b('0x3')) {
if (checkpass[_0x4b5b('0x2')](0x7, 0x9) == '{n') {
if (checkpass[_0x4b5b('0x2')](split * 0x2, split * 0x2 * 0x2) == _0x4b5b('0x4')) {
if (checkpass[_0x4b5b('0x2')](0x3, 0x6) == 'oCT') {
if (checkpass[_0x4b5b('0x2')](split * 0x3 * 0x2, split * 0x4 * 0x2) == _0x4b5b('0x5')) {
if (checkpass['substring'](0x6, 0xb) == 'F{not') {
if (checkpass[_0x4b5b('0x2')](split * 0x2 * 0x2, split * 0x3 * 0x2) == _0x4b5b('0x6')) {
if (checkpass[_0x4b5b('0x2')](0xc, 0x10) == _0x4b5b('0x7')) {
alert(_0x4b5b('0x8'));
}
}
}
}
}
}
}
} else {
alert(_0x4b5b('0x9'));
}
}
```
整理 `verify()` 裡面的條件式後,我們可以得出下面的資訊:
```javascript
checkpass[_0x4b5b('0x2')](0, 8) == _0x4b5b('0x3')
checkpass[_0x4b5b('0x2')](8, 16) == _0x4b5b('0x4')
checkpass[_0x4b5b('0x2')](24, 32) == _0x4b5b('0x5')
checkpass[_0x4b5b('0x2')](16, 24) == _0x4b5b('0x6')
```
為了知道 `_0x4b5b('0x3')` 等等的值,我們可以把相關的資料都丟入開發者工具提供的 console:
```javascript
var _0x5a46 = ['37115}', '_again_3', 'this', 'Password\x20Verified', 'Incorrect\x20password', 'getElementById', 'value', 'substring', 'picoCTF{', 'not_this'];
(function(_0x4bd822, _0x2bd6f7) {
var _0xb4bdb3 = function(_0x1d68f6) {
while (--_0x1d68f6) {
_0x4bd822['push'](_0x4bd822['shift']());
}
};
_0xb4bdb3(++_0x2bd6f7);
}(_0x5a46, 0x1b3));
var _0x4b5b = function(_0x2d8f05, _0x4b81bb) {
_0x2d8f05 = _0x2d8f05 - 0x0;
var _0x4d74cb = _0x5a46[_0x2d8f05];
return _0x4d74cb;
};
```
將上面的內容都送入 console 後,我們就可以直接訪問對應的值了:

透過這個方法,我們可以把 `_0x4b5b('0x3')`、`_0x4b5b('0x4')`、`_0x4b5b('0x6')` 和 `_0x4b5b('0x5')` 的值都找出來,接著組合在一起就是 flag 了。
### Summary
Flag: `picoCTF{not_this_again_xxxxxxxx}`
## JaWT Scratchpad <span style="color: orange;"> [Medium] </span>
### Challenge description
> Internal server errors can be intentionally returned by this challenge. If you experience one, try clearing your cookies.
Check the admin scratchpad! `https://jupiter.challenges.picoctf.org/problem/63090/` or http://jupiter.challenges.picoctf.org:63090
### Solution
進入網站後,會看到下面的畫面:

嘗試在下方的輸入框隨意輸入東西並送出後,會發現以對應的身份登入了 JaWT:

但若嘗試以 admin 身份登入則會得到警告訊息:
```
YOU CANNOT LOGIN AS THE ADMIN! HE IS SPECIAL AND YOU ARE NOT.
```
從這題的名稱跟提示我們可以知道是要從 JWT 下手,以任意一個使用者名稱登入後打開 cookie 也能看到我們的 jwt token。而我們的網頁下方有一個 John 的連結,將滑鼠移動到上面後可以看到它指向了 John the ripper 的網站,因此,從這些資訊我們可以推測是要使用 John the ripper 來破解 JWT 的 secret。
```bash
$ echo "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiMTIzIn0.jgZBxsEy_Mq68jv7XDm-sQr1BUOMHuV8c4dUIxHMT2k" > jwt.john
$ john jwt.john --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 128/128 ASIMD 4x])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ilovepico (?)
1g 0:00:00:01 DONE (2024-08-19 22:51) 0.7633g/s 5653Kp/s 5653Kc/s 5653KC/s in_199..ilovejesus789
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
```
有了 jwt 的 secret 之後,我們就可以構建新的 jwt token 了:

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.gtqDl4jVDvNbEe_JYEZTN19Vx6X9NNZtRVbKPBkhO-s
```
接著把 cookie 改掉後重整瀏覽器就可以拿到 flag 了。
### Summary
Flag: `picoCTF{jawt_was_just_what_you_thought_xxxxxxxx}`
## picobrowser <span style="color: orange;"> [Medium] </span>
### Challenge description
This website can be rendered only by **picobrowser**, go and catch the flag! `https://jupiter.challenges.picoctf.org/problem/26704/` ([link](https://jupiter.challenges.picoctf.org/problem/26704/)) or http://jupiter.challenges.picoctf.org:26704
### Solution
進入網站後可以看到一個寫著 flag 的按鈕,點擊之後會看到跳出一個警示框顯示我們不是 picobrowser 並且顯示我們目前的 User-Agent:
```
You're not picobrowser! Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
```
因此此處應該是判斷 User-Agent 是否為 picobrowser,我們要想辦法將 User-Agent 更改對應的值。
首先,我們可以先找到按下按鈕後會發送請求到哪個路徑:
```html
<a href="/flag" class="btn btn-lg btn-success btn-block"> Flag</a>
```
可以看到路徑為 `/flag`,因此我們可以用 `curl` 對這個路徑發送修改 User-Agent 後的請求:
```bash
$ curl https://jupiter.challenges.picoctf.org/problem/26704/flag -H "User-Agent: picobrowser"
```
接著就可以看到 flag 被包含在回傳的 response 中了。
### Summary
Flag: `picoCTF{p1c0_s3cr3t_ag3nt_xxxxxxxx}`
## Java Script Kiddie <span style="color: red;"> [Hard] </span>
### Challenge description
The image link appears broken... https://jupiter.challenges.picoctf.org/problem/58112 or http://jupiter.challenges.picoctf.org:58112
### Solution
網站上只有一個輸入框,而在輸入框中隨便輸入內容會出現一個損壞的圖片:

使用開發人員工具來查看原始碼:
```html
<script>
var bytes = [];
$.get("bytes", function(resp) {
bytes = Array.from(resp.split(" "), x => Number(x));
});
function assemble_png(u_in){
var LEN = 16;
var key = "0000000000000000";
var shifter;
if(u_in.length == LEN){
key = u_in;
}
var result = [];
for(var i = 0; i < LEN; i++){
shifter = key.charCodeAt(i) - 48;
for(var j = 0; j < (bytes.length / LEN); j ++){
result[(j * LEN) + i] = bytes[(((j + shifter) * LEN) % bytes.length) + i]
}
}
while(result[result.length-1] == 0){
result = result.slice(0,result.length-1);
}
document.getElementById("Area").src = "data:image/png;base64," + btoa(String.fromCharCode.apply(null, new Uint8Array(result)));
return false;
}
</script>
```
可以看到程式首先會向 `/bytes` 發送 GET 請求,拿到一串 data:
```
# http://jupiter.challenges.picoctf.org:58112/bytes
128 252 182 115 177 211 142 252 189 248 130 93 154 0 68 90 131 255 204 170 239 167 18 51 233 43 0 26 210 72 95 120 227 7 195 126 207 254 115 53 141 217 0 11 118 192 110 0 0 170 248 73 103 78 10 174 208 233 156 187 185 65 228 0 137 128 228 71 159 10 111 10 29 96 71 238 141 86 91 82 0 214 37 114 7 0 238 114 133 0 140 0 38 36 144 108 164 141 63 2 69 73 15 65 68 0 249 13 0 64 111 220 48 0 55 255 13 12 68 41 66 120 188 0 73 27 173 72 189 80 0 148 0 64 26 123 0 32 44 237 0 252 36 19 52 0 78 227 98 88 1 185 1 128 182 177 155 44 132 162 68 0 1 239 175 248 68 91 84 18 223 223 111 83 26 188 241 12 0 197 57 89 116 96 223 96 161 45 133 127 125 63 80 129 69 59 241 157 0 105 57 23 30 241 62 229 128 91 39 152 125 146 216 91 5 217 16 48 159 4 198 23 108 178 199 14 6 175 51 154 227 45 56 140 221 0 230 228 99 239 132 198 133 72 243 93 3 86 94 246 156 153 123 1 204 200 233 143 127 64 164 203 36 24 2 169 121 122 159 40 4 25 64 0 241 9 94 220 254 221 122 8 22 227 140 221 248 250 141 66 78 126 190 73 248 105 5 14 26 19 119 223 103 165 69 177 68 61 195 239 115 199 126 61 41 242 175 85 211 11 5 250 93 79 194 78 245 223 255 189 0 128 9 150 178 0 112 247 210 21 36 0 2 252 144 59 101 164 185 94 232 59 150 255 187 1 198 171 182 228 147 73 149 47 92 133 147 254 173 242 39 254 223 214 196 135 248 34 146 206 63 127 127 22 191 92 88 69 23 142 167 237 248 23 215 148 166 59 243 248 173 210 169 254 209 157 174 192 32 228 41 192 245 47 207 120 139 28 224 249 29 55 221 109 226 21 129 75 41 113 192 147 45 144 55 228 126 250 127 197 184 155 251 19 220 11 241 171 229 213 79 135 93 49 94 144 38 250 121 113 58 114 77 111 157 146 242 175 236 185 60 67 173 103 233 234 60 248 27 242 115 223 207 218 203 115 47 252 241 152 24 165 115 126 48 76 104 126 42 225 226 211 57 252 239 21 195 205 107 255 219 132 148 81 171 53 79 91 27 174 235 124 213 71 221 243 212 38 224 124 54 77 248 252 88 163 44 191 109 63 189 231 251 189 242 141 246 249 15 0 2 230 7 244 161 31 42 182 219 15 221 164 252 207 53 95 99 60 190 232 78 255 197 16 169 252 100 164 19 158 32 189 126 140 145 158 116 245 68 94 149 111 252 74 135 189 83 74 71 218 99 220 208 87 24 228 11 111 245 1 0 98 131 46 22 94 71 244 22 147 21 83 155 252 243 90 24 59 73 247 223 127 242 183 251 124 28 245 222 199 248 122 204 230 79 219 147 11 225 202 239 24 132 55 89 221 143 151 137 63 150 79 211 8 16 4 60 63 99 65 0 2
```
然後在使用者按下送出時將使用者的輸入送入 assemble_png 函式中作為 u_in 參數執行:
```html
<form action="#" onsubmit="assemble_png(document.getElementById('user_in').value)">
<input type="text" id="user_in">
<input type="submit" value="Submit">
</form>
<img id="Area" src=""/>
```
如果 u_in 的長度為 LEN,key 就會被賦值為 u_in,也就是我們在輸入框中輸入的內容,因此可以推測這題需要我們輸入正確的 key 來復原 PNG 圖片,並且 key 長度為 16 位。
```javascript
var LEN = 16;
var key = "0000000000000000";
var shifter;
if(u_in.length == LEN){
key = u_in;
}
<SNIP>
shifter = key.charCodeAt(i) - 48;
```
另外,程式中 shifter 的值為 key 第 i 位的 ascii value 減掉 48,而 ascii value 48 對應到的字符為 '0',因此這裡的目的像是在透過減掉 48 將原本的字符轉換成數字 ('0' -> 0),所以我們可以推測 key 的 charset 就在數字中。
而 PNG 圖片的前 16 個 bytes 為固定的數值:
```
# PNG Magic Number (8) + IHDR Length (4) + IHDR Chunk Type Code (4)
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52
```
```javascript
result[(j * LEN) + i] = bytes[(((j + shifter) * LEN) % bytes.length) + i]
```
當 j = 0 的時候,i 從 0 到 15 正好會對應上前 16 bytes,因此我們可以先移除 j 的值來暴力破解出 key,再透過 key 去恢復圖片。
```python
from PIL import Image
import requests
import itertools
import io
import os
LEN = 16
def get_bytes():
res = requests.get("http://jupiter.challenges.picoctf.org:58112/bytes")
bytes_data = list(map(int, res.text.split(" ")))
return bytes_data
def create_png(bytes_data, key):
result = [0] * len(bytes_data)
for i in range(LEN):
shifter = ord(key[i]) - 48
for j in range(len(bytes_data) // LEN):
result[(j * LEN) + i] = bytes_data[(((j + shifter) * LEN) % len(bytes_data)) + i]
img_bytes = io.BytesIO(bytes(result))
try:
img = Image.open(img_bytes)
img.save(os.path.join(os.getcwd(), "{}.png".format(key)))
print ("Key {} produces a valid PNG - Saving".format(key))
except IOError:
print ("Key {} produces an invalid PNG - Ignoring".format(key))
def main():
data = get_bytes()
expected = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52]
keys = []
for i in range(LEN):
print(f"\nIndex {i}: ", end="")
key_i = []
for shifter in range(10):
result = data[((shifter * LEN) % len(data)) + i]
if result == expected[i]:
print(shifter, end=" ")
key_i.append(str(shifter))
keys.append(key_i)
possibles = list(itertools.product(*keys))
print("\n\nPossible keys:")
for key in possibles:
create_png(data, "".join(key))
main()
```
```bash
Index 0: 4
Index 1: 8
Index 2: 9
Index 3: 4
Index 4: 7
Index 5: 4
Index 6: 8
Index 7: 4
Index 8: 8
Index 9: 5 6
Index 10: 1 2
Index 11: 6
Index 12: 7
Index 13: 1
Index 14: 0
Index 15: 4
Possible keys:
Key 4894748485167104 produces a valid PNG - Saving
Key 4894748485267104 produces an invalid PNG - Ignoring
Key 4894748486167104 produces an invalid PNG - Ignoring
Key 4894748486267104 produces an invalid PNG - Ignoring
```
執行後可以看到成功找到正確的 key 並且將圖片儲存下來了,而圖片是一個 qrcode:

最後掃描 qrcode 或使用 zbar-tools 之後就能得到 flag 了。
```bash
$ zbarimg 4894748485167104.png
Connection Error (Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory)
Connection Null
QR-Code:picoCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
scanned 1 barcode symbols from 1 images in 0.01 seconds
```
### Summary
Flag: `picoCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}`
### Resource
[PNG文件结构分析 - CSDN博客](https://blog.csdn.net/qq_60131542/article/details/123450382)
[Wikipedia - PNG](https://zh.wikipedia.org/zh-tw/PNG)
## Java Script Kiddie 2 <span style="color: red;"> [Hard] </span>
### Challenge description
The image link appears broken... twice as badly... https://jupiter.challenges.picoctf.org/problem/42899 or http://jupiter.challenges.picoctf.org:42899
### Solution
這題跟上一題很類似,只差在 key 跟 shifter 做出了一點變化:
```javascript
function assemble_png(u_in){
var LEN = 16;
var key = "00000000000000000000000000000000";
var shifter;
if(u_in.length == key.length){
key = u_in;
}
var result = [];
for(var i = 0; i < LEN; i++){
shifter = Number(key.slice((i*2),(i*2)+1));
for(var j = 0; j < (bytes.length / LEN); j ++){
result[(j * LEN) + i] = bytes[(((j + shifter) * LEN) % bytes.length) + i]
}
}
<SNIP>
}
```
這次 key 變成了 32 位,但實際上 shifter 只會使用到其中 index 為偶數的 16 位數,因此我們可以透過跟上一題一樣的方式先將偶數 index 位數爆破出來,然後在其他位填入占位符就好:
```python
from PIL import Image
from pyzbar.pyzbar import decode
import requests
import itertools
import io
import os
LEN = 16
def get_bytes():
res = requests.get("http://jupiter.challenges.picoctf.org:42899/bytes")
bytes_data = list(map(int, res.text.split(" ")))
return bytes_data
def create_png(bytes_data, key):
result = [0] * len(bytes_data)
for i in range(LEN):
shifter = int(key[i*2:(i*2)+1])
for j in range(len(bytes_data) // LEN):
result[(j * LEN) + i] = bytes_data[(((j + shifter) * LEN) % len(bytes_data)) + i]
img_bytes = io.BytesIO(bytes(result))
try:
img = Image.open(img_bytes)
img.save(os.path.join(os.getcwd(), "{}.png".format(key)))
print ("Key {} produces a valid PNG - Saving".format(key))
for obj in decode(img):
print(f'Decode: {obj.data.decode("utf-8")}')
except IOError:
print ("Key {} produces an invalid PNG - Ignoring".format(key))
def main():
data = get_bytes()
expected = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52]
keys = []
for i in range(LEN):
print(f"\nIndex {i}: ", end="")
key_i = []
for shifter in range(10):
result = data[((shifter * LEN) % len(data)) + i]
if result == expected[i]:
print(shifter, end=" ")
key_i.append(str(shifter))
keys.append(key_i)
possibles = list(itertools.product(*keys))
print("\n\nPossible keys:")
for key in possibles:
create_png(data, "0".join(key) + "0")
main()
```
```
$ python3 solve.py
Index 0: 7
Index 1: 6
Index 2: 1
Index 3: 6
Index 4: 0
Index 5: 7
Index 6: 9
Index 7: 1
Index 8: 5
Index 9: 0 1
Index 10: 0 1
Index 11: 0
Index 12: 2
Index 13: 0
Index 14: 8
Index 15: 5
Possible keys:
Key 70601060007090105000000020008050 produces a valid PNG - Saving
Key 70601060007090105000100020008050 produces an invalid PNG - Ignoring
Key 70601060007090105010000020008050 produces an invalid PNG - Ignoring
Key 70601060007090105010100020008050 produces an invalid PNG - Ignoring
```
### Summary
Flag: `picoCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}`
# Reverse Engineering
## vault-door-training <span style="color: green;"> [Easy] </span>
### Challenge description
Your mission is to enter Dr. Evil's laboratory and retrieve the blueprints for his Doomsday Project. The laboratory is protected by a series of locked vault doors. Each door is controlled by a computer and requires a password to open. Unfortunately, our undercover agents have not been able to obtain the secret passwords for the vault doors, but one of our junior agents obtained the source code for each vault's computer! You will need to read the source code for each level to figure out what the password is for that vault door. As a warmup, we have created a replica vault in our training facility. The source code for the training vault is here: [VaultDoorTraining.java](https://jupiter.challenges.picoctf.org/static/1afdf83322ee9c0040f8e3a3c047e18b/VaultDoorTraining.java)
### Solution
查看程式後,可以看到 `main()` 會接收使用者的輸入,然後將 "picoCTF{" 和 "}" 去掉後存入變數 `input`,並送入 `checkPassword()` 比對密碼,因此密碼應該就是我們的 flag。
```java
public static void main(String args[]) {
VaultDoorTraining vaultDoor = new VaultDoorTraining();
Scanner scanner = new Scanner(System.in);
System.out.print("Enter vault password: ");
String userInput = scanner.next();
String input = userInput.substring("picoCTF{".length(),userInput.length()-1);
if (vaultDoor.checkPassword(input)) {
System.out.println("Access granted.");
} else {
System.out.println("Access denied!");
}
}
```
接著前往 `checkPassword()` 查看剩下的 flag 部分的線索:
```java
public boolean checkPassword(String password) {
return password.equals("w4rm1ng_Up_w1tH_jAv4_xxxxxxxxxxx");
}
```
可以看到,剩餘的部分是直接以明文的方式寫出來,因此把這串密碼放入 "picoCTF{}" 裡面就是我們的 flag 了。
### Summary
Flag: `picoCTF{w4rm1ng_Up_w1tH_jAv4_xxxxxxxxxxx}`
## vault-door-1 <span style="color: orange;"> [Medium] </span>
### Challenge description
This vault uses some complicated arrays! I hope you can make sense of it, special agent. The source code for this vault is here: [VaultDoor1.java](https://jupiter.challenges.picoctf.org/static/29b91e638ccbd76aaa8c0462d1c64d8d/VaultDoor1.java)
### Solution
跟上題一樣,flag 的值藏在 `checkPassword()` 的函式裡面:
```java
public boolean checkPassword(String password) {
return password.length() == 32 &&
password.charAt(0) == 'd' &&
password.charAt(29) == '3' &&
password.charAt(4) == 'r' &&
password.charAt(2) == '5' &&
password.charAt(23) == 'r' &&
password.charAt(3) == 'c' &&
password.charAt(17) == '4' &&
password.charAt(1) == '3' &&
password.charAt(7) == 'b' &&
password.charAt(10) == '_' &&
password.charAt(5) == '4' &&
password.charAt(9) == '3' &&
password.charAt(11) == 't' &&
password.charAt(15) == 'c' &&
password.charAt(8) == 'l' &&
password.charAt(12) == 'H' &&
password.charAt(20) == 'c' &&
password.charAt(14) == '_' &&
password.charAt(6) == 'm' &&
password.charAt(24) == '5' &&
password.charAt(18) == 'r' &&
password.charAt(13) == '3' &&
password.charAt(19) == '4' &&
password.charAt(21) == 'T' &&
password.charAt(16) == 'H' &&
password.charAt(27) == 'f' &&
password.charAt(30) == 'b' &&
password.charAt(25) == '_' &&
password.charAt(22) == '3' &&
password.charAt(28) == '6' &&
password.charAt(26) == 'f' &&
password.charAt(31) == '0';
}
```
可以看到,`checkPassword()` 會檢查傳進來的參數 `password` 在每個 index 應該是什麼值,因此我們可以透過這個方式反推出 flag。
### Summary
Flag: `picoCTF{d35cr4mbl3_tH3_cH4r4cT3r5_xxxxxx}`
## vault-door-3 <span style="color: orange;"> [Medium] </span>
### Challenge description
This vault uses for-loops and byte arrays. The source code for this vault is here: [VaultDoor3.java](https://jupiter.challenges.picoctf.org/static/a648ca6dd275b9454c5d0de6d0f6efd3/VaultDoor3.java)
### Solution
跟前面一樣,flag 的資訊藏在 `checkPassword()` 中:
```java
public boolean checkPassword(String password) {
if (password.length() != 32) {
return false;
}
char[] buffer = new char[32];
int i;
for (i=0; i<8; i++) {
buffer[i] = password.charAt(i);
}
for (; i<16; i++) {
buffer[i] = password.charAt(23-i);
}
for (; i<32; i+=2) {
buffer[i] = password.charAt(46-i);
}
for (i=31; i>=17; i-=2) {
buffer[i] = password.charAt(i);
}
String s = new String(buffer);
return s.equals("jU5t_a_sna_3lpm18gb41_u_4_mfr340");
}
```
可以看到,程式用了幾個 for-loop 來將 `password` 參數中的值放到新的位置,接著與字串 "jU5t_a_sna_3lpm18gb41_u_4_mfr340" 比對,因此我們要做的事是將這個字串的每個值對應到原本在 `password` 裡面的時候的位置。
第一個 loop:
| password | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| -------- | - | - | - | - | - | - | - | - |
| buffer | j | U | 5 | t | _ | a | _ | s |
第二個 loop:
| password | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
| -------- | -- | -- | -- | -- | -- | -- | - | - |
| buffer | n | a | _ | 3 | l | p | m | 1 |
第三個 loop:
| password | 30 | 28 | 26 | 24 | 22 | 20 | 18 | 16 |
| -------- | -- | -- | -- | -- | -- | -- | -- | -- |
| buffer | 8 | b | 1 | u | 4 | m | r | 4 |
第四個 loop:
| password | 31 | 29 | 27 | 25 | 23 | 21 | 19 | 17 |
| -------- | -- | -- | -- | -- | -- | -- | -- | -- |
| buffer | 0 | 3 | f | _ | _ | _ | 4 | g |
有了 `password` 在每一個位置的值之後,將其組合在一起就可以拿到 flag 了。
### Summary
Flag: `picoCTF{jU5t_a_s1mpl3_an4gr4m_4_u_xxxxxx}`
## vault-door-4 <span style="color: orange;"> [Medium] </span>
### Challenge description
This vault uses ASCII encoding for the password. The source code for this vault is here: [VaultDoor4.java](https://jupiter.challenges.picoctf.org/static/09d3002ae349631324a17e2255ae8df2/VaultDoor4.java)
### Solution
首先查看 `checkPassword()`:
```java
public boolean checkPassword(String password) {
byte[] passBytes = password.getBytes();
byte[] myBytes = {
106 , 85 , 53 , 116 , 95 , 52 , 95 , 98 ,
0x55, 0x6e, 0x43, 0x68, 0x5f, 0x30, 0x66, 0x5f,
0142, 0131, 0164, 063 , 0163, 0137, 0143, 061 ,
'9' , '4' , 'f' , '7' , '4' , '5' , '8' , 'e' ,
};
for (int i=0; i<32; i++) {
if (passBytes[i] != myBytes[i]) {
return false;
}
}
return true;
}
```
可以看到,`password` 轉成 bytes 之後會與 `myBytes` 比較,因此只要將 `myBytes` 轉換回 ascii 文字,就能拿到 flag 了。在 `myBytes` 中,最後一行已經是英文字母了,而前面三行則分別是不同進制的數字:
```python
dec_num = [106, 85, 53, 116, 95, 52, 95, 98]
hex_num = [0x55, 0x6e, 0x43, 0x68, 0x5f, 0x30, 0x66, 0x5f]
oct_num = [0o142, 0o131, 0o164, 0o63, 0o163, 0o137, 0o143, 0o61]
```
在 python 中,我們用不同進制對應的樣式匯入後,python 會幫我們將其自動轉換成十進制數字,因此我們就可以直接用 `chr()` 將它們轉換回 ascii 字母:
```python
flag = "".join([chr(x) for x in dec_num + hex_num + oct_num]) + "94f7458e"
print(flag)
```
轉換後就可以拿到 flag 了。
### Summary
Flag: `picoCTF{jU5t_4_bUnCh_0f_bYt3s_xxxxxxxxxx}`
## vault-door-5 <span style="color: orange;"> [Medium] </span>
### Challenge description
In the last challenge, you mastered octal (base 8), decimal (base 10), and hexadecimal (base 16) numbers, but this vault door uses a different change of base as well as URL encoding! The source code for this vault is here: [VaultDoor5.java](https://jupiter.challenges.picoctf.org/static/0a53bf0deaba6919f98d8550c35aa253/VaultDoor5.java)
### Solution
跟前幾題一樣,首先打開程式查看 `checkPassword()`:
```java
public boolean checkPassword(String password) {
String urlEncoded = urlEncode(password.getBytes());
String base64Encoded = base64Encode(urlEncoded.getBytes());
String expected = "JTYzJTMwJTZlJTc2JTMzJTcyJTc0JTMxJTZlJTY3JTVm"
+ "JTY2JTcyJTMwJTZkJTVmJTYyJTYxJTM1JTY1JTVmJTM2"
+ "JTM0JTVmJTMwJTYyJTM5JTM1JTM3JTYzJTM0JTY2";
return base64Encoded.equals(expected);
}
```
可以看到,程式的過程中會將 `password` 進行 url 和 base64 的 encode,然後檢查是否跟 `expected` 變數相同。因此我們可以將 `expected` 變數的值依序進行 base64 和 url 的 decode 來得出 password 應該是什麼,就可以拿到我們的 flag 了。
Recipe: [CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)URL_Decode()&input=SlRZekpUTXdKVFpsSlRjMkpUTXpKVGN5SlRjMEpUTXhKVFpsSlRZM0pUVm1KVFkySlRjeUpUTXdKVFprSlRWbUpUWXlKVFl4SlRNMUpUWTFKVFZtSlRNMkpUTTBKVFZtSlRNd0pUWXlKVE01SlRNMUpUTTNKVFl6SlRNMEpUWTI)
### Summary
Flag: `picoCTF{c0nv3rt1ng_fr0m_ba5e_64_xxxxxxxx}`
## vault-door-6 <span style="color: orange;"> [Medium] </span>
### Challenge description
This vault uses an XOR encryption scheme. The source code for this vault is here: [VaultDoor6.java](https://jupiter.challenges.picoctf.org/static/cdb33ffba609e2521797aac66320ec65/VaultDoor6.java)
### Solution
首先檢查程式中的 `checkPassword()` 函式:
```java
public boolean checkPassword(String password) {
if (password.length() != 32) {
return false;
}
byte[] passBytes = password.getBytes();
byte[] myBytes = {
0x3b, 0x65, 0x21, 0xa , 0x38, 0x0 , 0x36, 0x1d,
0xa , 0x3d, 0x61, 0x27, 0x11, 0x66, 0x27, 0xa ,
0x21, 0x1d, 0x61, 0x3b, 0xa , 0x2d, 0x65, 0x27,
0xa , 0x6c, 0x60, 0x37, 0x30, 0x60, 0x31, 0x36,
};
for (int i=0; i<32; i++) {
if (((passBytes[i] ^ 0x55) - myBytes[i]) != 0) {
return false;
}
}
return true;
}
```
可以看到,程式過程中會將 `password` 轉換成 bytes,接著將每一個元素與 `0x55` 進行 xor 並比對是否與 `myBytes` 中的元素相同。因此我們只要將 `myBytes` 中的元素與 `0x55` 做 xor 然後轉換回 ascii 文字就可以拿到 flag 了。
```python
from pwn import *
flag_bytes = [0x3b, 0x65, 0x21, 0xa , 0x38, 0x0 , 0x36, 0x1d,
0xa , 0x3d, 0x61, 0x27, 0x11, 0x66, 0x27, 0xa ,
0x21, 0x1d, 0x61, 0x3b, 0xa , 0x2d, 0x65, 0x27,
0xa , 0x6c, 0x60, 0x37, 0x30, 0x60, 0x31, 0x36]
flag = "".join(xor(x, 0x55).decode() for x in flag_bytes)
```
### Summary
Flag: `picoCTF{n0t_mUcH_h4rD3r_tH4n_x0r_xxxxxxx}`
## vault-door-7 <span style="color: red;"> [Hard] </span>
### Challenge description
This vault uses bit shifts to convert a password string into an array of integers. Hurry, agent, we are running out of time to stop Dr. Evil's nefarious plans! The source code for this vault is here: [VaultDoor7.java](https://jupiter.challenges.picoctf.org/static/89b8065d19ee9830ae548d27a40ca757/VaultDoor7.java)
### Solution
查看程式後,我們可以知道 `password` 會被送到 `paswordToIntArray()` 中將 `password` 轉成 bytes,並以 4 個為一組分成 8 組進行位移:
```java
public int[] passwordToIntArray(String hex) {
int[] x = new int[8];
byte[] hexBytes = hex.getBytes();
for (int i=0; i<8; i++) {
x[i] = hexBytes[i*4] << 24
| hexBytes[i*4+1] << 16
| hexBytes[i*4+2] << 8
| hexBytes[i*4+3];
}
return x;
}
public boolean checkPassword(String password) {
if (password.length() != 32) {
return false;
}
int[] x = passwordToIntArray(password);
return x[0] == 1096770097
&& x[1] == 1952395366
&& x[2] == 1600270708
&& x[3] == 1601398833
&& x[4] == 1716808014
&& x[5] == 1734291511
&& x[6] == 960049251
&& x[7] == 1681089078;
}
```
程式是以 4 個 bytes 為一組進行位移來轉換成一個十進制數值。因此我們可以將十進制數值轉換成二進制或十六進制,然後拆回四組 (位移是以 8 個位元為單位進行位移,因此拆分時每 8 個二進制位元為一組,或每 2 個十六進制字母或數字為一組),來還原原本的 bytes 值。例如:
```
1096770097 = 0b1000001010111110110001000110001
=> ['01000001', '01011111', '01100010', '00110001']
= 0x415f6231
=> ["41", "5f", "62", "31"]
```
我們可以寫一個簡單的 script 來進行轉換。將十進制數值轉成十六進制後拆分成四個字串,接著使用 `chr()` 將每個字串轉換成 ascii 文字,最後在組合再一起就可以拿到 flag 了:
```python
int_array = [1096770097, 1952395366, 1600270708, 1601398833, 1716808014, 1734291511, 960049251, 1681089078]
flag = ""
for num in int_array:
hex_str = f"{num:08x}"
for i in range(0, 8, 2):
flag += chr(int(hex_str[i:i+2], 16))
print(flag)
```
### Summary
Flag: `picoCTF{A_b1t_0f_b1t_sh1fTiNg_xxxxxxxxxx}`
## vault-door-8 <span style="color: red;"> [Hard] </span>
### Challenge description
Apparently Dr. Evil's minions knew that our agency was making copies of their source code, because they intentionally sabotaged this source code in order to make it harder for our agents to analyze and crack into! The result is a quite mess, but I trust that my best special agent will find a way to solve it. The source code for this vault is here: [VaultDoor8.java](https://jupiter.challenges.picoctf.org/static/9b13abb1479aa3979db28a9083712663/VaultDoor8.java)
### Solution
題目提供的的程式全部擠在一起,直接丟到 Reformat 工具中。處理完後可以看到主要的判斷程式:
```java
public char[] scramble(String password) {/* Scramble a password by transposing pairs of bits. */
char[] a = password.toCharArray();
for (int b = 0; b < a.length; b++) {
char c = a[b];
c = switchBits(c, 1, 2);
c = switchBits(c, 0, 3); /* c = switchBits(c,14,3); c = switchBits(c, 2, 0); */
c = switchBits(c, 5, 6);
c = switchBits(c, 4, 7);
c = switchBits(c, 0, 1); /* d = switchBits(d, 4, 5); e = switchBits(e, 5, 6); */
c = switchBits(c, 3, 4);
c = switchBits(c, 2, 5);
c = switchBits(c, 6, 7);
a[b] = c;
}
return a;
}
public boolean checkPassword(String password) {
char[] scrambled = scramble(password);
char[] expected = {
0xF4, 0xC0, 0x97, 0xF0, 0x77, 0x97, 0xC0, 0xE4, 0xF0, 0x77, 0xA4, 0xD0, 0xC5, 0x77, 0xF4, 0x86, 0xD0, 0xA5, 0x45, 0x96, 0x27, 0xB5, 0x77, 0xE0, 0x95, 0xF1, 0xE1, 0xE0, 0xA4, 0xC0, 0x94, 0xA4};
return Arrays.equals(scrambled, expected);
}
```
可以看到程式中會將 flag 的每個字元裡面的 bits 進行交換,然後跟 `expected` 比較,所以只要將 `scramble()` 裡面交換位元的順序倒著執行回去就可以還原了。
solve script:
```java
class VaultDoor8 {
public static void main(String args[]) {
VaultDoor8 a = new VaultDoor8();
char[] expected = {
0xF4, 0xC0, 0x97, 0xF0, 0x77, 0x97, 0xC0, 0xE4, 0xF0, 0x77, 0xA4, 0xD0, 0xC5, 0x77, 0xF4, 0x86, 0xD0, 0xA5, 0x45, 0x96, 0x27, 0xB5, 0x77, 0xE0, 0x95, 0xF1, 0xE1, 0xE0, 0xA4, 0xC0, 0x94, 0xA4};
System.out.println(a.scramble_rev(expected));
}
public String scramble_rev(char[] scrambled) {
for (int b = 0; b < scrambled.length; b++) {
char c = scrambled[b];
c = switchBits(c, 6, 7);
c = switchBits(c, 2, 5);
c = switchBits(c, 3, 4);
c = switchBits(c, 0, 1);
c = switchBits(c, 4, 7);
c = switchBits(c, 5, 6);
c = switchBits(c, 0, 3);
c = switchBits(c, 1, 2);
scrambled[b] = c;
}
return new String(scrambled);
}
public char switchBits(char c, int p1, int p2) {/* Move the bit in position p1 to position p2, and move the bit
that was in position p2 to position p1. Precondition: p1 < p2 */
char mask1 = (char) (1 << p1);
char mask2 = (char) (1 << p2); /* char mask3 = (char)(1<<p1<<p2); mask1++; mask1--; */
char bit1 = (char) (c & mask1);
char bit2 = (char) (c & mask2); /* System.out.println("bit1 " + Integer.toBinaryString(bit1));
System.out.println("bit2 " + Integer.toBinaryString(bit2)); */
char rest = (char) (c & ~(mask1 | mask2));
char shift = (char) (p2 - p1);
char result = (char) ((bit1 << shift) | (bit2 >> shift) | rest);
return result;
}
}
```
### Summary
Flag: `picoCTF{s0m3_m0r3_b1t_sh1fTiNg_xxxxxxxx}`
## asm1 <span style="color: orange;"> [Medium] </span>
### Challenge description
What does asm1(0x6fa) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. [Source](https://jupiter.challenges.picoctf.org/static/b41e08fc19ceb9d0466ebd68d36c5630/test.S)
### Solution
打開題目提供的 source code 之後可以看到:
```assembly
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: cmp DWORD PTR [ebp+0x8],0x3a2
<+10>: jg 0x512 <asm1+37>
<+12>: cmp DWORD PTR [ebp+0x8],0x358
<+19>: jne 0x50a <asm1+29>
<+21>: mov eax,DWORD PTR [ebp+0x8]
<+24>: add eax,0x12
<+27>: jmp 0x529 <asm1+60>
<+29>: mov eax,DWORD PTR [ebp+0x8]
<+32>: sub eax,0x12
<+35>: jmp 0x529 <asm1+60>
<+37>: cmp DWORD PTR [ebp+0x8],0x6fa
<+44>: jne 0x523 <asm1+54>
<+46>: mov eax,DWORD PTR [ebp+0x8]
<+49>: sub eax,0x12
<+52>: jmp 0x529 <asm1+60>
<+54>: mov eax,DWORD PTR [ebp+0x8]
<+57>: add eax,0x12
<+60>: pop ebp
<+61>: ret
```
從題目描述跟 source code 可以得出 0x6fa 應該是作為參數一起被傳入程式中執行,並且被存放在 `[ebp+0x8]` 的地方,而我們要找的 flag 是程式最後回傳的值,也就是 `eax` 在程式結束時的值。
從程式的開頭開始查看,首先會比較 0x6fa 是否大於 0x3a2,如果是的話就跳到 `<asm1+37>`:
```assembly
<+3>: cmp DWORD PTR [ebp+0x8],0x3a2
<+10>: jg 0x512 <asm1+37>
```
條件成立,接著跳到 `<asm1+37>` 的部分繼續查看,可以看到程式中判斷如果 `[ebp+0x8]`(0x6fa) 不等於 0x6fa,就跳到 `<asm1+54>`:
```assembly
<+37>: cmp DWORD PTR [ebp+0x8],0x6fa
<+44>: jne 0x523 <asm1+54>
```
因為條件不成立,因此接續下去執行 `<asm1+46>` 的指令:
```assembly
<+46>: mov eax,DWORD PTR [ebp+0x8]
<+49>: sub eax,0x12
<+52>: jmp 0x529 <asm1+60>
```
這裡的指令會把 `eax` 賦值為 `[ebp+0x8]`(0x6fa),然後將 `eax` 減去 0x12,並跳到 `<asm1+60>` 的地方。`<asm1+60>` 後面接續一條指令程式就結束了,因此 `eax` 的值就是 0x6fa - 0x12 = 0x6e8。
### Summary
Flag: `0x6e8`
## asm2 <span style="color: red;"> [Hard] </span>
### Challenge description
What does asm2(0x4,0x21) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. [Source](https://jupiter.challenges.picoctf.org/static/7e3eb2f90200ac88126f62ceb4bc3948/test.S)
### Solution
這次程式傳入 0x4 和 0x21,並且分別存放在 `[ebp+0x8]` 和 `[ebp+0xc]` 的位置:
```assembly
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: sub esp,0x10
<+6>: mov eax,DWORD PTR [ebp+0xc]
<+9>: mov DWORD PTR [ebp-0x4],eax
<+12>: mov eax,DWORD PTR [ebp+0x8]
<+15>: mov DWORD PTR [ebp-0x8],eax
<+18>: jmp 0x509 <asm2+28>
<+20>: add DWORD PTR [ebp-0x4],0x1
<+24>: add DWORD PTR [ebp-0x8],0x74
<+28>: cmp DWORD PTR [ebp-0x8],0xfb46
<+35>: jle 0x501 <asm2+20>
<+37>: mov eax,DWORD PTR [ebp-0x4]
<+40>: leave
<+41>: ret
```
可以看到,前面的幾條指令中會透過 `eax` 傳遞數值,將 `[ebp+0xc]` (0x21) 的值存入 `[ebp-0x4]` 中,把 `[ebp+0x8]` (0x4) 的值存入 `[ebp-0x8]`:
```assembly
<+6>: mov eax,DWORD PTR [ebp+0xc]
<+9>: mov DWORD PTR [ebp-0x4],eax
<+12>: mov eax,DWORD PTR [ebp+0x8]
<+15>: mov DWORD PTR [ebp-0x8],eax
```
接下來則是會進行一段迴圈:
```assembly
<+18>: jmp 0x509 <asm2+28>
<+20>: add DWORD PTR [ebp-0x4],0x1
<+24>: add DWORD PTR [ebp-0x8],0x74
<+28>: cmp DWORD PTR [ebp-0x8],0xfb46
<+35>: jle 0x501 <asm2+20>
```
對應到 python 相當於下面的程式:
```python
while ebp_0x8 <= 0xfb46:
ebp_0x4 += 0x1
ebp_0x8 += 0x74
```
最後進行計算後就可以知道 `[ebp-0x4]` 的值,也就是最後 eax 的值,進而拿到 flag 了:
```python
ebp_0x4 = 0x21
ebp_0x8 = 0x4
while ebp_0x8 <= 0xfb46:
ebp_0x4 += 0x1
ebp_0x8 += 0x74
eax = ebp_0x4
print(hex(eax))
```
### Summary
Flag: `0x24c`
## asm3 <span style="color: red;"> [Hard] </span>
### Challenge description
What does asm3(0xd73346ed,0xd48672ae,0xd3c8b139) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. [Source](https://jupiter.challenges.picoctf.org/static/17c5620fcffa388fe518d31cb4dd99a0/test.S)
### Solution
這次的程式進行比較複雜的運算:
```assembly
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: xor eax,eax
<+5>: mov ah,BYTE PTR [ebp+0xa]
<+8>: shl ax,0x10
<+12>: sub al,BYTE PTR [ebp+0xc]
<+15>: add ah,BYTE PTR [ebp+0xd]
<+18>: xor ax,WORD PTR [ebp+0x10]
<+22>: nop
<+23>: pop ebp
<+24>: ret
```
我們可以直接編譯程式來得到計算結果:
test.S:
```assembly
.intel_syntax noprefix
.global asm3
asm3:
push ebp
mov ebp,esp
xor eax,eax
mov ah,BYTE PTR [ebp+0xa]
shl ax,0x10
sub al,BYTE PTR [ebp+0xc]
add ah,BYTE PTR [ebp+0xd]
xor ax,WORD PTR [ebp+0x10]
nop
pop ebp
ret
```
main.c:
```c
#include <stdio.h>
int asm3(int, int, int);
int main(int argc, char* argv[])
{
printf("0x%x\n", asm3(0xd73346ed,0xd48672ae,0xd3c8b139));
return 0;
}
```
編譯:
```bash
$ gcc -masm=intel -m32 -c test.S -o test.o
$ gcc -m32 -c main.c -o main.o
$ gcc -m32 test.o main.o -o main
```
如果遇到下面的錯誤可以參考這篇 [fatal error: bits/libc-header-start.h: No such file or directory" while compiling HTK](https://stackoverflow.com/questions/54082459/fatal-error-bits-libc-header-start-h-no-such-file-or-directory-while-compili):
```bash
In file included from main.c:1:
/usr/include/stdio.h:28:10: fatal error: bits/libc-header-start.h: No such file or directory
28 | #include <bits/libc-header-start.h>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
```
安裝 32-bit 的 header 跟 libraries 來解決:
```bash
$ sudo apt install gcc-multilib
```
最後執行編譯完的程式就能拿到計算結果了。
### Summary
Flag: `0xc36b`
## asm4 <span style="color: red;"> [Hard] </span>
### Challenge description
What will asm4("picoCTF_a3112") return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. [Source](https://jupiter.challenges.picoctf.org/static/80186ad81f4a1569b480e7fbf68b29ca/test.S)
### Solution
這題提供的程式跟上一題一樣比較複雜,所以一樣直接編譯程式來解決:
```assembly
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: push ebx
<+4>: sub esp,0x10
<+7>: mov DWORD PTR [ebp-0x10],0x246
<SNIP>
<+155>: pop ebx
<+156>: pop ebp
<+157>: ret
```
其中程式中因為有跳轉的部分,因此要更改 `test.S` 把跳轉的地址改成跳轉到 label:
```assembly
.intel_syntax noprefix
.global asm4
asm4:
push ebp
mov ebp,esp
push ebx
sub esp,0x10
mov DWORD PTR [ebp-0x10],0x246
mov DWORD PTR [ebp-0xc],0x0
jmp asm4_27
asm4_23:
add DWORD PTR [ebp-0xc],0x1
asm4_27:
mov edx,DWORD PTR [ebp-0xc]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
test al,al
jne asm4_23
mov DWORD PTR [ebp-0x8],0x1
jmp asm4_138
asm4_51:
mov edx,DWORD PTR [ebp-0x8]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
movsx edx,al
mov eax,DWORD PTR [ebp-0x8]
lea ecx,[eax-0x1]
mov eax,DWORD PTR [ebp+0x8]
add eax,ecx
movzx eax,BYTE PTR [eax]
movsx eax,al
sub edx,eax
mov eax,edx
mov edx,eax
mov eax,DWORD PTR [ebp-0x10]
lea ebx,[edx+eax*1]
mov eax,DWORD PTR [ebp-0x8]
lea edx,[eax+0x1]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
movsx edx,al
mov ecx,DWORD PTR [ebp-0x8]
mov eax,DWORD PTR [ebp+0x8]
add eax,ecx
movzx eax,BYTE PTR [eax]
movsx eax,al
sub edx,eax
mov eax,edx
add eax,ebx
mov DWORD PTR [ebp-0x10],eax
add DWORD PTR [ebp-0x8],0x1
asm4_138:
mov eax,DWORD PTR [ebp-0xc]
sub eax,0x1
cmp DWORD PTR [ebp-0x8],eax
jl asm4_51
mov eax,DWORD PTR [ebp-0x10]
add esp,0x10
pop ebx
pop ebp
ret
```
main.c:
```c
#include <stdio.h>
int asm4(char[]);
int main(int argc, char* argv[]) {
printf("0x%x\n", asm4("picoCTF_a3112"));
return 0;
}
```
然後一樣編譯之後執行就能拿到 flag 了:
```bash
$ gcc -masm=intel -m32 -c test.S -o test.o
$ gcc -m32 -c main.c -o main.o
$ gcc -m32 test.o main.o -o main
$ ./main
```
### Summary
Flag: `0x1d0`
## droids0 <span style="color: red;"> [Hard] </span>
### Challenge description
Where do droid logs go. Check out this [file](https://jupiter.challenges.picoctf.org/static/02bcd73e630f50ef0b12bcdad9d84e0d/zero.apk).
### Solution
將題目提供的 apk 檔用 android studio 開啟,並用 jadx decompile 程式,可以在 `MainActivity` 看到 `buttonClick()` 會呼叫 `getFlag()`:
```java
public void buttonClick(View view) {
this.text_bottom.setText(FlagstaffHill.getFlag(this.text_input.getText().toString(), this.ctx));
}
```
而 `getFlag()` 則會將 `paprika()` 產出的結果輸出到 log:
```java
public static String getFlag(String input, Context ctx) {
Log.i("PICO", paprika(input));
return "Not Today...";
}
```
所以可以猜測按下按鈕的時候,程式會輸出 flag 到 log console,我們可以用 android studio 中內建的模擬器打開程式:

打開下方的 Logcat panel,並點下按鈕後就能看到 flag 被印出來了:

### Summary
Flag: `picoCTF{a.moose.once.bit.my.sister}`
## droids1 <span style="color: red;"> [Hard] </span>
### Challenge description
Find the pass, get the flag. Check out this [file](https://jupiter.challenges.picoctf.org/static/b12c6d058c7f52eb1fd2015cfd291716/one.apk).
### Solution
跟上題一樣,將 apk 檔匯入 android studio,並用 jadx plugin 查看反編譯後的結果:
```java
public static String getFlag(String input, Context ctx) {
if (input.equals(ctx.getString(R.string.password))) {
return fenugreek(input);
}
return "NOPE";
}
```
可以看到,這次 `getFlag()` 會檢查輸入是否等於 `R.string.password`,而這可以在 resources 裡面的 string 找到:

一樣用模擬器執行程式,輸入密碼 `opossum` 並按下按鈕觸發 `getFlag()` 後就可以看到 flag 了:

### Summary
Flag: `picoCTF{pining.for.the.fjords}`
## droids2 <span style="color: red;"> [Hard] </span>
### Challenge description
Find the pass, get the flag. Check out this [file](https://jupiter.challenges.picoctf.org/static/b7d30de6eaaf83e685aea7c10c5bdea8/two.apk).
### Solution
跟上題一樣,用 jadx 查看反編譯後的程式碼:
```java
public static String getFlag(String input, Context ctx) {
String[] witches = {"weatherwax", "ogg", "garlick", "nitt", "aching", "dismass"};
int second = 3 - 3;
int third = (3 / 3) + second;
int fourth = (third + third) - second;
int fifth = 3 + fourth;
if (input.equals("".concat(witches[fifth]).concat(".").concat(witches[third]).concat(".").concat(witches[second]).concat(".").concat(witches[(fifth + second) - third]).concat(".").concat(witches[3]).concat(".").concat(witches[fourth]))) {
return sesame(input);
}
return "NOPE";
}
```
可以看到,這次的 pass 是一連串的字串組合而成,我們可以將檢查 pass 的地方改為輸出,讓 java 幫我們印出 pass:
```java
public class Main {
public static void main(String[] args) {
String[] witches = {"weatherwax", "ogg", "garlick", "nitt", "aching", "dismass"};
int second = 3 - 3;
int third = (3 / 3) + second;
int fourth = (third + third) - second;
int fifth = 3 + fourth;
System.out.print("".concat(witches[fifth]).concat(".").concat(witches[third]).concat(".").concat(witches[second]).concat(".").concat(witches[(fifth + second) - third]).concat(".").concat(witches[3]).concat(".").concat(witches[fourth]));
}
}
```
執行後,可以知道 pass 是 `dismass.ogg.weatherwax.aching.nitt.garlick`,再丟到 app 中就能拿到 flag 了:

### Summary
Flag: `picoCTF{what.is.your.favourite.colour}`
## droids3 <span style="color: red;"> [Hard] </span>
### Challenge description
Find the pass, get the flag. Check out this [file](https://jupiter.challenges.picoctf.org/static/06318765139795831859f843dd56ce60/three.apk).
### Solution
跟上題一樣,用 jadx 查看反編譯後的程式碼:
```java
public class FlagstaffHill {
public static native String cilantro(String str);
public static String nope(String input) {
return "don't wanna";
}
public static String yep(String input) {
return cilantro(input);
}
public static String getFlag(String input, Context ctx) {
return nope(input);
}
}
```
我們需要更改 `getFlag()` 裡面的程式,使其返回 `yep(input)` 的結果來拿到 flag。可以用 `apktool` 工具將 apk 檔解包:
```bash
$ aptkool d -f -r three.apk
```
然後修改 `three/smali/com/hellocmu/picoctf/FlagstaffHill.smali` 裡面的內容:
```diff
- invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->nope(Ljava/lang/String;)Ljava/lang/String;
+ invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->yep(Ljava/lang/String;)Ljava/lang/String;
```
打包:
```bash
$ apktool b -f -r three -o three_new.apk
```
最後還要進行 align & sign,可以用 uber-apk-signer 幫我們快速完成:
```bash
$ wget -P ~/Documents/tools/Apk/ https://github.com/patrickfav/uber-apk-signer/releases/download/v1.3.0/uber-apk-signer-1.3.0.jar
$ $ java -jar ~/Documents/tools/Apk/uber-apk-signer-1.3.0.jar -apk three_new.apk
```
最後將處理完的 apk 丟入模擬器中並點擊 app 裡的按鈕就能拿到 flag 了:

### Summary
Flag: `picoCTF{tis.but.a.scratch}`
### Resource
[Android App 逆向入門之一:拆開與重組 apk](https://blog.huli.tw/2023/04/27/android-apk-decompile-intro-1/)
[Github - uber-apk-signer](https://github.com/patrickfav/uber-apk-signer)
## droids4 <span style="color: red;"> [Hard] </span>
### Challenge description
Reverse the pass, patch the file, get the flag. Check out this [file](https://jupiter.challenges.picoctf.org/static/926d4bfd7030b13dbc98ca26e608c740/four.apk).
### Solution
跟上題一樣,用 jadx 查看反編譯後的程式碼:
```java
public class FlagstaffHill {
public static native String cardamom(String str);
public static String getFlag(String input, Context ctx) {
StringBuilder ace = new StringBuilder("aaa");
StringBuilder jack = new StringBuilder("aaa");
StringBuilder queen = new StringBuilder("aaa");
StringBuilder king = new StringBuilder("aaa");
ace.setCharAt(0, (char) (ace.charAt(0) + 4));
ace.setCharAt(1, (char) (ace.charAt(1) + 19));
ace.setCharAt(2, (char) (ace.charAt(2) + 18));
jack.setCharAt(0, (char) (jack.charAt(0) + 7));
jack.setCharAt(1, (char) (jack.charAt(1) + 0));
jack.setCharAt(2, (char) (jack.charAt(2) + 1));
queen.setCharAt(0, (char) (queen.charAt(0) + 0));
queen.setCharAt(1, (char) (queen.charAt(1) + 11));
queen.setCharAt(2, (char) (queen.charAt(2) + 15));
king.setCharAt(0, (char) (king.charAt(0) + 14));
king.setCharAt(1, (char) (king.charAt(1) + 20));
king.setCharAt(2, (char) (king.charAt(2) + 15));
if (input.equals("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()))) {
return "call it";
}
return "NOPE";
}
}
```
可以看到我們需要將 `return "call it";` 更改為 `return cardamom(input);`。跟上一題一樣,先將 apk 解開,然後修改 `four/smali/com/hellocmu/picoctf/FlagstaffHill.smali`:
```diff
- const-string v5, "call it"
+ invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->cardamom(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v5
```
patch 完檔案就可以將其打包並丟到模擬器中了,但我們還要找到 pass。跟 droids2 一樣,我們可以直接把檢查密碼的邏輯修改成印出密碼,然後讓 java 幫我們印出:
```java
public class Main {
public static void main(String[] args) {
StringBuilder ace = new StringBuilder("aaa");
StringBuilder jack = new StringBuilder("aaa");
StringBuilder queen = new StringBuilder("aaa");
StringBuilder king = new StringBuilder("aaa");
ace.setCharAt(0, (char) (ace.charAt(0) + 4));
ace.setCharAt(1, (char) (ace.charAt(1) + 19));
ace.setCharAt(2, (char) (ace.charAt(2) + 18));
jack.setCharAt(0, (char) (jack.charAt(0) + 7));
jack.setCharAt(1, (char) (jack.charAt(1) + 0));
jack.setCharAt(2, (char) (jack.charAt(2) + 1));
queen.setCharAt(0, (char) (queen.charAt(0) + 0));
queen.setCharAt(1, (char) (queen.charAt(1) + 11));
queen.setCharAt(2, (char) (queen.charAt(2) + 15));
king.setCharAt(0, (char) (king.charAt(0) + 14));
king.setCharAt(1, (char) (king.charAt(1) + 20));
king.setCharAt(2, (char) (king.charAt(2) + 15));
System.out.print("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()));
}
}
```
執行後,可以知道 pass 是 `alphabetsoup`,丟到 app 中點擊按鈕就能拿到 flag 了:

### Summary
Flag: `picoCTF{not.particularly.silly}`
## reverse_cipher <span style="color: red;"> [Hard] </span>
### Challenge description
We have recovered a [binary](https://jupiter.challenges.picoctf.org/static/31c9b832d036a10daeef52d8b4290ef0/rev) and a [text file](https://jupiter.challenges.picoctf.org/static/31c9b832d036a10daeef52d8b4290ef0/rev_this). Can you reverse the flag.
### Solution
將 binary 丟到 ida decompile:
```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
char ptr[23]; // [rsp+0h] [rbp-50h] BYREF
char v5; // [rsp+17h] [rbp-39h]
int v6; // [rsp+2Ch] [rbp-24h]
FILE *v7; // [rsp+30h] [rbp-20h]
FILE *stream; // [rsp+38h] [rbp-18h]
int j; // [rsp+44h] [rbp-Ch]
int i; // [rsp+48h] [rbp-8h]
char v11; // [rsp+4Fh] [rbp-1h]
stream = fopen("flag.txt", "r");
v7 = fopen("rev_this", "a");
if ( !stream )
puts("No flag found, please make sure this is run on the server");
if ( !v7 )
puts("please run this on the server");
v6 = fread(ptr, 0x18uLL, 1uLL, stream);
if ( v6 <= 0 )
exit(0);
for ( i = 0; i <= 7; ++i )
{
v11 = ptr[i];
fputc(v11, v7);
}
for ( j = 8; j <= 22; ++j )
{
v11 = ptr[j];
if ( (j & 1) != 0 )
v11 -= 2;
else
v11 += 5;
fputc(v11, v7);
}
v11 = v5;
fputc(v5, v7);
fclose(v7);
return fclose(stream);
}
```
可以看到,程式會讀入 `flag.txt` 然後處理裡面的內容後寫入 `rev_this`。
其中前 7 個字元不變,後面 8~23 個字元,如果是 index 是奇數就輸出其 ascii number - 2 的字元,否則輸出其 ascii number + 5 的字元。所以就根據 index 是否為奇數計算原本的 ascii number 並輸出成字元就可以拿到 flag 了。
solve script:
```python
rev_this = "picoCTF{w1{1wq85jc=2i0<}"
flag_content = ""
for j in range(8, 23):
offset = -5 if j % 2 == 0 else 2
flag_content += chr(ord(rev_this[j]) + offset)
print(f"picoCTF{{{flag_content}}}")
```
### Summary
Flag: `picoCTF{r3v3rs3xxxxxxxx}`
## Need For Speed <span style="color: red;"> [Hard] </span>
### Challenge description
The name of the game is [speed](https://www.youtube.com/watch?v=8piqd2BWeGI). Are you quick enough to solve this problem and keep it above 50 mph? [need-for-speed](https://jupiter.challenges.picoctf.org/static/27dd5548b14661f65ce3ac6a8a8f575b/need-for-speed).
### Solution
將程式丟到 ida 裡面 decompile 後可以看到程式會用 alarm 來讓程式報錯:
```c
unsigned int set_timer()
{
if ( __sysv_signal(14, alarm_handler) == (__sighandler_t)-1LL )
{
puts("\n\nSomething bad happened here. ");
exit(0);
}
return alarm(1u);
}
```
我們可以丟到 gdb 設定 `handle SIGALRM ignore` 讓無視 SIGALRM 繼續執行:
```bash
gef➤ handle SIGALRM ignore
Signal Stop Print Pass to program Description
SIGALRM No Yes No Alarm clock
gef➤ r
Starting program: /home/ls0723000/need-for-speed
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Keep this thing over 50 mph!
============================
Creating key...
Program received signal SIGALRM, Alarm clock.
Finished
Printing flag:
PICOCTF{Good job keeping bus #xxxxxxxx speeding along!}
[Inferior 1 (process 1691) exited normally]
```
然後就能拿到 flag 了。
### Summary
Flag: `PICOCTF{Good job keeping bus #xxxxxxxx speeding along!}`
## Forky <span style="color: red;"> [Hard] </span>
### Challenge description
In [this program](https://jupiter.challenges.picoctf.org/static/30d2f832b77911e86b909e16c6f6232d/vuln), identify the last integer value that is passed as parameter to the function doNothing().
### Solution
將程式丟到 ida 裡面 decompile 後可以看到程式會進行 4 次 fork:
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
_DWORD *v4; // [esp+8h] [ebp-Ch]
v4 = (_DWORD *)mmap(0, 4, 3, 33, -1, 0);
*v4 = 1000000000;
fork();
fork();
fork();
fork();
*v4 += 1234567890;
doNothing(*v4);
return 0;
}
```
所以 `v4` 的值為 `1000000000 + 2 ^ 4 * 1234567890`,而因為 C 存在溢位的情況,所以 `v4` 最後的值會是溢位後的結果。我們可以用 numpy 幫我們計算溢位後的數字:
```python
import numpy as np
print(np.int32(1000000000 + 2 ** 4 * 1234567890))
```
最後計算出來的結果就是 flag 了。
### Summary
Flag: `picoCTF{-721750240}`
# Cryptography
## The Numbers <span style="color: green;"> [Easy] </span>
### Challenge description
The [numbers](https://jupiter.challenges.picoctf.org/static/f209a32253affb6f547a585649ba4fda/the_numbers.png)... what do they mean?
### Solution
題目給的圖片:

16, 9, 3, 15, 3, 20, 6 照順序對應到 a-z 中會是 "picoCTF",所以可以知道圖中每個數字代表的是在 a-z 中的第幾位。
solve script:
```python
import string
flag_list = [16, 9, 3, 15, 3, 20, 6, "{", 20, 8, 5, 14, 21, 13, 2, 5, 18, 19, 13, 1, 19, 15, 14, "}"]
flag = "".join([string.ascii_lowercase[x-1] if isinstance(x, int) else x for x in flag_list])
print(flag)
```
### Summary
Flag: `picoctf{thenumbersxxxxx}`
## 13 <span style="color: green;"> [Easy] </span>
### Challenge description
Cryptography can be easy, do you know what ROT13 is? `cvpbPGS{abg_gbb_onq_bs_n_ceboyrz}`
### Solution
rot 13 轉回去就可以拿到 flag 了。
```bash
echo "cvpbPGS{abg_gbb_onq_bs_n_ceboyrz}" | tr "a-zA-Z" "n-za-mN-ZA-M"
```
### Summary
Flag: `picoCTF{not_too_bad_of_a_xxxxxxx}`
## caesar <span style="color: orange;"> [Medium] </span>
### Challenge description
Decrypt this [message](https://jupiter.challenges.picoctf.org/static/49f31c8f17817dc2d367428c9e5ab0bc/ciphertext).
### Solution
凱薩密碼只要直接爆破 26 種可能就可以知道 flag 了:
```python
import string
def num2char(num):
return string.ascii_lowercase[num]
def char2num(char):
return string.ascii_lowercase.index(char)
cipher = open("ciphertext", "r").read().strip()
for i in range(26):
decrypt = "".join([num2char((char2num(c) + i) % 26) for c in cipher[8:-1]])
print(f"Shift: {i}")
print(f"picoCTF{{{decrypt}}}")
```
執行後可以發現 shift 4 位的時候可以解出有意義的文字,所以那個就是 flag。
### Summary
Flag: `picoCTF{crossingtherubiconvfhsjkou}`
## Easy1 <span style="color: orange;"> [Medium] </span>
### Challenge description
The one time pad can be cryptographically secure, but not when you know the key. Can you solve this? We've given you the encrypted flag, key, and a table to help `UFJKXQZQUNB` with the key of `SOLVECRYPTO`. Can you use this [table](https://jupiter.challenges.picoctf.org/static/1fd21547c154c678d2dab145c29f1d79/table.txt) to solve it?.
### Solution
從題目提供的 table 中可以知道這個是維吉尼亞密碼,直接用線上工具解開就可以拿到 flag 了:

### Summary
Flag: `picoCTF{cryptoisxxx}`
### Resource
[在线计算器: 维吉尼亚密码](https://zh.planetcalc.com/2468/)
## Mr-Worldwide <span style="color: orange;"> [Medium] </span>
### Challenge description
A musician left us a [message](https://jupiter.challenges.picoctf.org/static/d5570d48262dbba2a31f2a940409ad9d/message.txt). What's it mean?
### Solution
message 的內容:
```
picoCTF{(35.028309, 135.753082)(46.469391, 30.740883)(39.758949, -84.191605)(41.015137, 28.979530)(24.466667, 54.366669)(3.140853, 101.693207)_(9.005401, 38.763611)(-3.989038, -79.203560)(52.377956, 4.897070)(41.085651, -73.858467)(57.790001, -152.407227)(31.205753, 29.924526)}
```
裡面每組數字看起來是經緯度,丟到 google 後會對應到不同的地方,像是 (35.028309, 135.753082) 為京都:

嘗試一下後發現把每個經緯度對應到的地區的第一個英文字母塞到 message 中的對應位置就是 flag 了。
### Summary
Flag: `picoCTF{KODIAK_ALASKA}`
## waves over lambda <span style="color: orange;"> [Medium] </span>
### Challenge description
We made a lot of substitutions to encrypt this. Can you decrypt it? Connect with `nc jupiter.challenges.picoctf.org 43522`.
### Solution
連上 server 後會拿到一段經過多次替換的文字,可以丟到 quipqiup 中讓他幫我們分析:

可以看到,flag 就在分析完的文章中。
### Summary
Flag: `frequency_is_c_over_lambda_xxxxxxxxx`
## Flags <span style="color: orange;"> [Medium] </span>
### Challenge description
What do the [flags](https://jupiter.challenges.picoctf.org/static/fbeb5f9040d62b18878d199cdda2d253/flag.png) mean?
### Solution
搜尋題目提供的圖片裡面的每個圖案後,會發現它們是跟航海有關的訊號旗:


所以只要照順序將其一一對應回去就能拿到 flag 了。
### Summary
Flag: `PICOCTF{F1AG5AND5TUFF}`
## Tapping <span style="color: orange;"> [Medium] </span>
### Challenge description
Theres tapping coming in from the wires. What's it saying `nc jupiter.challenges.picoctf.org 9422`.
### Solution
連上 server 後會收到一串摩斯密碼,將其解回去就能拿到 flag 了。
```
.--. .. -.-. --- -.-. - ..-. { -- ----- .-. ... ...-- -.-. ----- -.. ...-- .---- ... ..-. ..- -. ..--- -.... ---.. ...-- ---.. ..--- ....- -.... .---- ----- }
```
### Summary
Flag: `PICOCTF{M0RS3C0D31SFUN2683824610}`
### Resource
[Morse Code Translator](https://morsecode.world/international/translator.html)
## john_pollard <span style="color: orange;"> [Medium] </span>
### Challenge description
Sometimes RSA [certificates](https://jupiter.challenges.picoctf.org/static/c882787a19ed5d627eea50f318d87ac5/cert) are breakable
### Solution
將題目提供的憑證下載下來後,用 `openssl` 來查看憑證的內容:
```bash
openssl x509 -in cert -text -noout
```
可以看到輸出結果中包含了憑證的模數,而且數值很小:
```
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 12345 (0x3039)
Signature Algorithm: md2WithRSAEncryption
Issuer: CN=PicoCTF
Validity
Not Before: Jul 8 07:21:18 2019 GMT
Not After : Jun 26 17:34:38 2019 GMT
Subject: OU=PicoCTF, O=PicoCTF, L=PicoCTF, ST=PicoCTF, C=US, CN=PicoCTF
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (53 bit)
Modulus: 4966306421059967 (0x11a4d45212b17f)
Exponent: 65537 (0x10001)
<SNIP>
```
直接用 FactorDB 分解 p 和 q:
```python
from factordb.factordb import FactorDB
n = 4966306421059967
f = FactorDB(n)
f.connect()
p, q = f.get_factor_list()
print(p, q)
```
### Summary
Flag: `picoCTF{73176001,67867967}`
### Resource
[FactorDB](https://factordb.com/)
## la cifra de <span style="color: orange;"> [Medium] </span>
### Challenge description
I found this cipher in an old book. Can you figure out what it says? Connect with `nc jupiter.challenges.picoctf.org 32411`.
### Solution
連上 server 後會收到一串加密過的訊息,丟到 dCode 的密碼辨識功能中,出來的結果告訴我們可能是透過維吉尼雅密碼進行加密的:

最後把密文整個維吉尼雅破解工具中就可以看到 flag 被破解出來了:

### Summary
Flag: `picoCTF{b311a50_0r_v1gn3r3_c1ph3rxxxxxxxx}`
### Resource
[dCode.fr - cipher identifier](https://www.dcode.fr/cipher-identifier)
[dCode.fr - vigenere cipher](https://www.dcode.fr/vigenere-cipher)
## AES-ABC <span style="color: red;"> [Hard] </span>
### Challenge description
AES-ECB is bad, so I rolled my own cipher block chaining mechanism - Addition Block Chaining! You can find the source here: [aes-abc.py](https://jupiter.challenges.picoctf.org/static/1b1beb0a49c958d0f2423800eaff58a7/aes-abc.py). The AES-ABC flag is [body.enc.ppm](https://jupiter.challenges.picoctf.org/static/1b1beb0a49c958d0f2423800eaff58a7/body.enc.ppm)
### Solution
在[維基百科](https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F#%E7%94%B5%E5%AD%90%E5%AF%86%E7%A0%81%E6%9C%AC%EF%BC%88ECB%EF%BC%89)中有關於 AES-ECB 的介紹,可以知道因為 ECB 會將相同的明文塊加密成相同的密文塊,所以在圖片上的效果不是很好。
而這題提供的程式主要是在 ECB 加密後的結果上,額外插入一個 iv,並根據 iv 將 block 從前到後額外進行一些運算:
```python
def aes_abc_encrypt(pt):
cipher = AES.new(KEY, AES.MODE_ECB)
ct = cipher.encrypt(pad(pt))
blocks = [ct[i * BLOCK_SIZE:(i+1) * BLOCK_SIZE] for i in range(len(ct) / BLOCK_SIZE)]
iv = os.urandom(16)
blocks.insert(0, iv)
for i in range(len(blocks) - 1):
prev_blk = int(blocks[i].encode('hex'), 16)
curr_blk = int(blocks[i+1].encode('hex'), 16)
n_curr_blk = (prev_blk + curr_blk) % UMAX
blocks[i+1] = to_bytes(n_curr_blk)
ct_abc = "".join(blocks)
return iv, ct_abc, ct
```
所以只要根據 iv 逆著運算回去拿到 AES-ECB 加密後的結果就可以了:
```python
BLOCK_SIZE = 16
UMAX = int(pow(256, BLOCK_SIZE))
with open('body.enc.ppm', 'rb') as f:
header = f.readline() + f.readline() + f.readline()
data = f.read()
blocks = [int.from_bytes(data[i * BLOCK_SIZE:(i + 1) * BLOCK_SIZE], 'big') for i in range(len(data) // BLOCK_SIZE)]
blocks_ecb = [(blocks[i + 1] - blocks[i]) % UMAX for i in range(len(blocks) - 1)]
with open('flag.enc.ppm', 'wb') as fw:
fw.write(header + b''.join(b.to_bytes(BLOCK_SIZE, 'big') for b in blocks_ecb))
```
然後查看 `flag.enc.ppm`,就會在圖片中看到 flag 了。
### Summary
Flag: `picoCTF{d0Nt_r0ll_yoUr_xxxxxxx}`
## miniRSA <span style="color: red;"> [Hard] </span>
### Challenge description
Let's decrypt this: [ciphertext](https://jupiter.challenges.picoctf.org/static/eb5e6df8e14c52873cf88c582a1a4008/ciphertext)? Something seems a bit small.
### Solution
從題目提供的檔案中我們可以知道 e 很小,在 $m^3 < 3$ 的這種情況直接開三次方根就可以算出原本的 m 了:
```
N: 29331922499794985782735976045591164936683059380558950386560160105740343201513369939006307531165922708949619162698623675349030430859547825708994708321803705309459438099340427770580064400911431856656901982789948285309956111848686906152664473350940486507451771223435835260168971210087470894448460745593956840586530527915802541450092946574694809584880896601317519794442862977471129319781313161842056501715040555964011899589002863730868679527184420789010551475067862907739054966183120621407246398518098981106431219207697870293412176440482900183550467375190239898455201170831410460483829448603477361305838743852756938687673
e: 3
ciphertext (c): 2205316413931134031074603746928247799030155221252519872650080519263755075355825243327515211479747536697517688468095325517209911688684309894900992899707504087647575997847717180766377832435022794675332132906451858990782325436498952049751141
```
solve script:
```python
from Crypto.Util.number import long_to_bytes
from gmpy2 import iroot
n = 29331922499794985782735976045591164936683059380558950386560160105740343201513369939006307531165922708949619162698623675349030430859547825708994708321803705309459438099340427770580064400911431856656901982789948285309956111848686906152664473350940486507451771223435835260168971210087470894448460745593956840586530527915802541450092946574694809584880896601317519794442862977471129319781313161842056501715040555964011899589002863730868679527184420789010551475067862907739054966183120621407246398518098981106431219207697870293412176440482900183550467375190239898455201170831410460483829448603477361305838743852756938687673
e = 3
c = 2205316413931134031074603746928247799030155221252519872650080519263755075355825243327515211479747536697517688468095325517209911688684309894900992899707504087647575997847717180766377832435022794675332132906451858990782325436498952049751141
if iroot(c, e)[1]:
flag = long_to_bytes(iroot(c, e)[0]).decode()
print(flag)
```
### Summary
Flag: `picoCTF{n33d_a_lArg3r_e_xxxxxxxx}`
## rsa-pop-quiz <span style="color: red;"> [Hard] </span>
### Challenge description
Class, take your seats! It's PRIME-time for a quiz... `nc jupiter.challenges.picoctf.org 1981`
### Solution
連上後會問一連串 RSA 相關的問題,用下面幾個式子推一下就可以拿到答案了:
```python
n = p * q
totient = (p - 1) * (q - 1)
d = pow(e, -1, totient)
c = pow(m, e, n)
m = pow(c, d, n)
```
最後把最後一題算出來的 plaintext 轉回 ascii 就可以拿到 flag 了。
### Summary
Flag: `picoCTF{wA8_th4t$_ill3aGal..xxxxxxxx}`
## b00tl3gRSA2 <span style="color: red;"> [Hard] </span>
### Challenge description
In RSA d is a lot bigger than e, why don't we use d to encrypt instead of e? Connect with `nc jupiter.challenges.picoctf.org 18243`.
### Solution
連上 server 後拿到了 c, n, e:
```
c: 85834293497533957436691992989020145290174917906572213883475460283218874518185414171477722376162890834272787612272208790956043659375474973614441338273240261639172686105889571933866352008105723235934748641303453747950363275260597624690025603876753519587312921763952214001042760070828009167397038688462585711830
n: 110226030519809485542323932026957431014099119244128173357812506357988443046556456458550282116546605853127651981223036687438540005337731383782535070798396214464486298384427058695821560827447632804183216216766668183743562783379289376673704531204537391206988134190705855419834086747623709523313516906678585633541
e: 98772359038968089132002648216230969546439400275415646684838412818493176294232586454083684298311374062539139995747215703239439383759844835366234907041479061207152704364095985876480151312903436976618451732664257250426519407276922219451731308809687133615933983163203332136556556102138190489500297473639358434337
```
當 e 過大或過小的時候,可以嘗試 wiener attack:
```bash
$ ~/Documents/tools/RSA/RsaCtfTool/RsaCtfTool.py --attack wiener -e 114145856108999468617578389354940683809841467770192254995042463330482468949478183941051398448574746309195970901049225962924695212052634021531893790526880257876936774702711357317886020530380791629595234846295809279723653706575293076619640669581799333759392559655499642461370320292695769337278820501227121212565 -n 131285463080948003278114369757546332897730476356243130440148434026953362832206380037989601799270689332685918901775383407314644357106640386604933633136662269510574389095061577705121535102908597559793129287218760857865913764213542398113966331431886151277774350412212579973046963871901899845844675676088368136041 --decrypt 23870756143632295978013372570068029840766716521632911970317057063737505739180212077602945699358205656535876075402152990481797815151911129377241185295872512145264119171758540192548132058854799123769463478143436417196802648916429895670744188850484554489796410004342496143198471566766924306623164392655062388748
```
```
utf-8 : picoCTF{bad_1d3a5_xxxxxxx}
```
### Summary
Flag: `picoCTF{bad_1d3a5_xxxxxxx}`
## b00tl3gRSA3 <span style="color: red;"> [Hard] </span>
### Challenge description
Why use p and q when I can use more? Connect with `nc jupiter.challenges.picoctf.org 3726`.
### Solution
連上 server 後拿到了 c, n, e:
```
c: 1011168439494949362061752987788733119558905451276054868796130963896349359920420360844631269358743618291058119498272262062798437075344286334819376763877241678884636918842529601936565100475981287468025687397122504749534472632525008282846673051820975476540348106377708858057178485913461089153666373468806144237601031982637619090468286700880847550
n: 54983018958565765822988458978042802633131867054148292059426571545071247927270298814283567276214772584132370818275936618531180131646572423656589460847636129927900948922808480176535390166186195940693053145057983226148971832697028086858683074435400469433035314258655433910950462494444935260369597343595990198827097154974158371383285440184106195781
e: 65537
```
這次這題的 n 有多個質數 factor,我們可以利用 Alpertron caculator 幫我們計算 totient:

有了 totient 之後就可以算出 d 並解出 flag 了:
```python
import gmpy2
from Crypto.Util.number import long_to_bytes
c = 1011168439494949362061752987788733119558905451276054868796130963896349359920420360844631269358743618291058119498272262062798437075344286334819376763877241678884636918842529601936565100475981287468025687397122504749534472632525008282846673051820975476540348106377708858057178485913461089153666373468806144237601031982637619090468286700880847550
n = 54983018958565765822988458978042802633131867054148292059426571545071247927270298814283567276214772584132370818275936618531180131646572423656589460847636129927900948922808480176535390166186195940693053145057983226148971832697028086858683074435400469433035314258655433910950462494444935260369597343595990198827097154974158371383285440184106195781
e = 65537
phi = 54983018811128826841550483380894706385201304812442825712792139639855072851463189249456773561322958606377525048209429466230874399798572720395808424013871873826738056871058308436045450838187009537335022842604469832022418736022000690711851293311647813614531170310007706703257007897855604656621875327809478170983612913069551167203180544000000000000
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m).decode())
```
或是也可以用 `sympy` 計算 totient:
```python
from sympy.ntheory import factorint
n = 54983018958565765822988458978042802633131867054148292059426571545071247927270298814283567276214772584132370818275936618531180131646572423656589460847636129927900948922808480176535390166186195940693053145057983226148971832697028086858683074435400469433035314258655433910950462494444935260369597343595990198827097154974158371383285440184106195781
f = factorint(n)
phi = 1
for p in f.keys():
phi *= (p - 1)
```
### Summary
Flag: `picoCTF{too_many_fact0rs_xxxxxxx}`
### Resource
[Integer factorization calculator](https://www.alpertron.com.ar/ECM.HTM)
# Forensics
## Glory of the Garden <span style="color: green;"> [Easy] </span>
### Challenge description
This [garden](https://jupiter.challenges.picoctf.org/static/d0e1ffb10fc0017c6a82c57900f3ffe3/garden.jpg) contains more than it seems.
### Solution
用 string + grep 就會發現 flag 以明文直接藏在圖片裡:
```bash
$ strings garden.jpg | grep pico
Here is a flag "picoCTF{more_than_m33ts_the_3y3xxxxxxxx}"
```
### Summary
Flag: `picoCTF{more_than_m33ts_the_3y3xxxxxxxx}`
## shark on wire 1 <span style="color: orange;"> [Medium] </span>
### Challenge description
We found this [packet capture](https://jupiter.challenges.picoctf.org/static/483e50268fe7e015c49caf51a69063d0/capture.pcap). Recover the flag.
### Solution
用 wireshark 打開 pcap 之後,會發現有不少 udp 流量。右鍵點擊 Follow > UDP Stream 查看每個 Stream 的內容就會看到 flag 了。
### Summary
Flag: `picoCTF{StaT31355_xxxxxxxx}`
## shark on wire 2 <span style="color: orange;"> [Medium] </span>
### Challenge description
We found this [packet capture](https://jupiter.challenges.picoctf.org/static/b506393b6f9d53b94011df000c534759/capture.pcap). Recover the flag that was pilfered from the network.
### Solution
可以看到,從 `10.0.0.66` 發出的 udp 請求的 port,後三位剛好是 flag 的 ascii number:


```
112 -> p
105 -> i
99 -> c
111 -> o
67 -> C
84 -> T
```
所以就寫個 script 把 port 都抓出來轉回字母就可以了:
```python
import pyshark
capture = pyshark.FileCapture('./capture.pcap', display_filter='ip.addr == 10.0.0.66')
data = []
for pkt in capture:
if pkt.udp.port != '5000':
data.append(chr(int(pkt.udp.port[1:])))
print("".join(data))
```
### Summary
Flag: `picoCTF{p1LLf3r3d_data_xxxxxxxxx}`
## like1000 <span style="color: orange;"> [Medium] </span>
### Challenge description
This [.tar file](https://jupiter.challenges.picoctf.org/static/52084b5ad360b25f9af83933114324e0/1000.tar) got tarred a lot.
### Solution
把 1000 層的 .tar 檔解開會出現 flag.png,打開來之後就可以看到 flag 了。
```bash
$ for i in $(seq 1000 -1 1); do tar -xf "$i.tar"; done
```
### Summary
Flag: `picoCTF{l0t5_0f_TAR5}`
## What Lies Within <span style="color: orange;"> [Medium] </span>
### Challenge description
There's something in the [building](https://jupiter.challenges.picoctf.org/static/011955b303f293d60c8116e6a4c5c84f/buildings.png). Can you retrieve the flag?
### Solution
用 zsteg 工具查看就可以看到 flag 了:
```bash
$ zsteg buildings.png
```
```
b1,rgb,lsb,xy .. text: "picoCTF{h1d1ng_1n_xxxxxxxx}"
```
### Summary
Flag: `picoCTF{h1d1ng_1n_xxxxxxxx}`
## c0rrupt <span style="color: orange;"> [Medium] </span>
### Challenge description
We found this [file](https://jupiter.challenges.picoctf.org/static/ab30fcb7d47364b4190a7d3d40edb551/mystery). Recover the flag.
### Solution
從題目的 hint 中可以知道我們要修復檔案的 header。查看檔案的 hex 值後,可以看到前面幾個 bytes 很接近 PNG file signature:
```bash
$ cat mystery | xxd | head
```
```
00000000: 8965 4e34 0d0a b0aa 0000 000d 4322 4452 .eN4........C"DR
00000010: 0000 066a 0000 0447 0802 0000 007c 8bab ...j...G.....|..
00000020: 7800 0000 0173 5247 4200 aece 1ce9 0000 x....sRGB.......
00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000 ..gAMA......a...
00000040: 0009 7048 5973 aa00 1625 0000 1625 0149 ..pHYs...%...%.I
00000050: 5224 f0aa aaff a5ab 4445 5478 5eec bd3f R$......DETx^..?
00000060: 8e64 cd71 bd2d 8b20 2080 9041 8302 08d0 .d.q.-. ..A....
00000070: f9ed 40a0 f36e 407b 9023 8f1e d720 8b3e ..@..n@{.#... .>
00000080: b7c1 0d70 0374 b503 ae41 6bf8 bea8 fbdc ...p.t...Ak.....
00000090: 3e7d 2a22 336f de5b 55dd 3d3d f920 9188 >}*"3o.[U.==. ..
```
```
PNG File Signature: 89 50 4E 47 0D 0A 1A 0A
```
根據這篇文章 [PNG structure for beginner](https://medium.com/@0xwan/png-structure-for-beginner-8363ce2a9f73) 中的內容,我們可以先將 png file signature 和 IHDR 修復回去:
```bash
$ hexeditor mystery
```

接下來檢查 `pngcheck` 來檢查圖片中的每個 chunk:
```bash
$ pngcheck -v mystery
File: mystery (202940 bytes)
chunk IHDR at offset 0x0000c, length 13
1642 x 1095 image, 24-bit RGB, non-interlaced
chunk sRGB at offset 0x00025, length 1
rendering intent = perceptual
chunk gAMA at offset 0x00032, length 4: 0.45455
chunk pHYs at offset 0x00042, length 9: 2852132389x5669 pixels/meter
CRC error in chunk pHYs (computed 38d82c82, expected 495224f0)
ERRORS DETECTED in mystery
```
從錯誤中可以看到 pHYs chunk 的 CRC 計算結果與預期的不同,並且 `pixels/meter` 的大小為 `2852132389x5669`,看起來不太合理,所以我們可以檢查一下 pHYs 整個 chunk。
pHYs 在 offset 0x42 的位置,根據它的結構比對 data:

```bash
$ xxd -g 1 -s 0x42 -l $((4+4+4+1+4)) mystery
```

一般 Pixels per units 在 X, Y axis 應該會相同,且 X axis 的值明顯過大,因此更改 X axis 的值使其與 Y axis 相同:

接著再重新檢查一次檔案:
```bash
$ pngcheck -v mystery
File: mystery (202940 bytes)
chunk IHDR at offset 0x0000c, length 13
1642 x 1095 image, 24-bit RGB, non-interlaced
chunk sRGB at offset 0x00025, length 1
rendering intent = perceptual
chunk gAMA at offset 0x00032, length 4: 0.45455
chunk pHYs at offset 0x00042, length 9: 5669x5669 pixels/meter (144 dpi)
: invalid chunk length (too large)
ERRORS DETECTED in mystery
```
訊息告訴我們 pHYs 後的 chunk 大小過大。根據 pHYs 的結構可以算出來下一個 chunk 在 offset `0x42 + 0x4 (Type) + 0x9 (Data) + 0x4 (CRC) = 0x53` 的地方,透過查看 `0x53` 後的 8 個 bytes 來確定該 chunk 的 Length 和 Type:
```bash
$ xxd -g 1 -s 0x53 -l 8 mystery
00000053: aa aa ff a5 ab 44 45 54 .....DET
```
IDAT 的 type byte 是 `49 44 41 54`,與上面的最為接近,因此先將對應的數值更改回去:

接下來用 `binwalk` 來確定這個區塊的大小:
```bash
$ binwalk -R "IDAT" mystery
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
87 0x57 Raw signature (IDAT)
65544 0x10008 Raw signature (IDAT)
131080 0x20008 Raw signature (IDAT)
196616 0x30008 Raw signature (IDAT)
```
所以當前 IDAT chunk 的大小為:
```
0x10008 - 0x4 (current chunk type) - 0x4 (current chunk CRC) - 0x4 (next chunk length) - 0x57 (next IDAT start offset) = 0xFFA5
```

更改完再檢查就會發現沒有錯誤了,這時候將檔案加上 `.png` 打開後就能看到 flag 了。
```bash
$ pngcheck -v mystery
File: mystery (202940 bytes)
chunk IHDR at offset 0x0000c, length 13
1642 x 1095 image, 24-bit RGB, non-interlaced
chunk sRGB at offset 0x00025, length 1
rendering intent = perceptual
chunk gAMA at offset 0x00032, length 4: 0.45455
chunk pHYs at offset 0x00042, length 9: 5669x5669 pixels/meter (144 dpi)
chunk IDAT at offset 0x00057, length 65445
zlib: deflated, 32K window, fast compression
chunk IDAT at offset 0x10008, length 65524
chunk IDAT at offset 0x20008, length 65524
chunk IDAT at offset 0x30008, length 6304
chunk IEND at offset 0x318b4, length 0
No errors detected in mystery (9 chunks, 96.3% compression)
```

### Summary
Flag: `picoCTF{c0rrupt10n_1847995}`
## extensions <span style="color: orange;"> [Medium] </span>
### Challenge description
This is a really weird text file [TXT](https://jupiter.challenges.picoctf.org/static/e7e5d188621ee705ceeb0452525412ef/flag.txt)? Can you find the flag?
### Solution
用 `file` 檢查檔案會發現其實是一個 PNG 檔:
```bash
$ file flag.txt
flag.txt: PNG image data, 1697 x 608, 8-bit/color RGB, non-interlaced
```
將檔案的 ext 改回 `.png`,然後再打開就能看到 flag 了。
### Summary
Flag: `picoCTF{now_you_know_about_xxxxxxxxxx}`
## WhitePages <span style="color: orange;"> [Medium] </span>
### Challenge description
I stopped using YellowPages and moved onto WhitePages... but [the page they gave me](https://jupiter.challenges.picoctf.org/static/fa4a277cfa846e07a5981d8a19288a2e/whitepages.txt) is all blank!
### Solution
讀取檔案後會發現內容中只有空格,而如果用 python 讀取內容並轉換成 `set`,則可以看到這些空格是由兩種字元組成的:
```python
$ python3
Python 3.12.8 (main, Dec 13 2024, 13:19:48) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> content = open('whitepages.txt', 'r').read()
>>> set(content)
{' ', '\u2003'}
```
可以猜測其中這兩個分別代表二進制中的 0 / 1,所以接著將空格轉換成二進制數值然後再轉換回文字:
```python
from Crypto.Util.number import long_to_bytes
content = open('whitepages.txt', 'r').read()
bin_value = "".join("1" if x == " " else "0" for x in content)
print(long_to_bytes(int(bin_value, 2)).decode())
```
然後就可以在轉換出來的文字中看到 flag 了。
### Summary
Flag: `picoCTF{not_all_spaces_are_created_equal_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}`
## So Meta <span style="color: orange;"> [Medium] </span>
### Challenge description
Find the flag in this [picture](https://jupiter.challenges.picoctf.org/static/89b371a46702a31aa9931a2a2b12f8bf/pico_img.png).
### Solution
直接用 `string` + `grep` 就可以看到 flag 了:
```bash
$ strings pico_img.png | grep pico
picoCTF{s0_m3ta_xxxxxxxx}
```
### Summary
Flag: `picoCTF{s0_m3ta_xxxxxxxx}`
## m00nwalk <span style="color: orange;"> [Medium] </span>
### Challenge description
Decode this [message](https://jupiter.challenges.picoctf.org/static/d6fcea5e3c6433680ea4f914e24fab61/message.wav) from the moon.
### Solution
從 hint 中可以推測是指 [Slow-scan television (sstv)](https://en.wikipedia.org/wiki/Slow-scan_television):
- How did pictures from the moon landing get sent back to Earth?
所以我們可以透過 `qsstv` 工具來將音訊轉回影像:
```bash
$ sudo apt update && sudo apt install qsstv pavucontrol
```
首先使用 `pavucontrol` 進行設定:
```bash
$ pactl load-module module-null-sink sink_name=virtual-cable
$ pavucontrol
```

需確定設定中有出現 Null Output device (上圖是在虛擬機中運行,顯示為 virtual-cable device),然後打開 `qsstv`(不要關掉 `pavucontrol`):
```bash
$ qsstv
```
選擇 `Options > Configuration > Sound` 並將 Audio Interface 設定為 PulseAudio:

以及回到 `pavucontrol` 中 `Recording` 的介面,設定 QSSTV 為 `Monitor of {前面設定的 Null Output device}`:

最後執行指令播放題目提供的音檔就可以看到圖片顯示在 `qsstv` 上了:
```bash
$ paplay -d virtual-cable message.wav
```

### Summary
Flag: `picoCTF{beep_boop_im_xxxxxxxx}`
### Resource
[How to convert (decode) a Slow-Scan Television transmissions (SSTV) audio file to images using QSSTV in Ubuntu 18.04](https://ourcodeworld.com/articles/read/956/how-to-convert-decode-a-slow-scan-television-transmissions-sstv-audio-file-to-images-using-qsstv-in-ubuntu-18-04)
## m00nwalk2 <span style="color: red;"> [Hard] </span>
### Challenge description
Revisit the last transmission. We think this [transmission](https://jupiter.challenges.picoctf.org/static/599404f0bf7426a5a5c2deb538860cda/message.wav) contains a hidden message. There are also some clues [clue 1](https://jupiter.challenges.picoctf.org/static/599404f0bf7426a5a5c2deb538860cda/clue1.wav), [clue 2](https://jupiter.challenges.picoctf.org/static/599404f0bf7426a5a5c2deb538860cda/clue2.wav), [clue 3](https://jupiter.challenges.picoctf.org/static/599404f0bf7426a5a5c2deb538860cda/clue3.wav).
### Solution
跟上一題一樣,先使用 `qsstv` 和 `pavucontrol` 將四個音檔都解出來。
message:

clue1:

clue2:

clue3:

搜尋 clue3 中的文字 "Alan Eliasen the FutureBoy" 後可以找到一個隱寫術相關的網站:


可以看到,網站上寫著:
> These pages use the steghide program to perform steganography, and the files generated are fully compatible with steghide.
所以我們可以用 `steghide` 來代替在網站上 decode(密碼可以從 clue1 解出來的圖片中看到):
```bash
$ steghide extract -sf message.wav -p hidden_stegosaurus
wrote extracted data to "steganopayload12154.txt".
```
最後執行指令後就可以從結果檔中拿到 flag 了。
### Summary
Flag: `picoCTF{the_answer_lies_hidden_in_xxxxxxxxxxx}`