---
# System prepended metadata

title: PicoCTF - Java Script Kiddie
tags: [Web, PicoCTF, CTF]

---

# PicoCTF - Java Script Kiddie
## Background
[JavaScript Array slice()](https://www.w3schools.com/jsref/jsref_slice_array.asp)
> ```javascript
> const fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
> const citrus = fruits.slice(1, 3);
> # output: Orange,Lemon
> ```

[JavaScript Uint8Array.from() Method](https://www.geeksforgeeks.org/javascript-uint8array-from-method/)
> ```javascript
> let array = Uint8Array.from('45465768654323456');
> console.log(array);
> # output: Uint8Array(17) [
> 4, 5, 4, 6, 5, 7, 6,
> 8, 6, 5, 4, 3, 2, 3,
> 4, 5, 6
> ]
> ```

[JavaScript String.fromCharCode()](https://www.w3schools.com/jsref/jsref_fromcharcode.asp)
> ```javascript
> let text = String.fromCharCode(65);
> console.log(text)
> # output: A
> ```


## Source code
:::spoiler Source Code
```htmlmixed=
<html>
	<head>    
		<script src="jquery-3.3.1.min.js"></script>
		<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>
	</head>
	<body>

		<center>
			<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=""/>
		</center>

	</body>
</html>
```
:::
## Recon
這一題很難，應該說scramble的方式很難看出來，而且他結合了web(js)/reverse/misc的各種概念在裡面，討論的WP也比較少，沒有甚麼script可以參考，但[^pico-reverse-java-script-kiddle-wp-walkthru]這一部影片討論的非常詳細，再搭配自己寫的script就可以尻出原本的圖檔
1. 首先題目的頁面剛載入的時候，會直接傳送一個bytes的矩陣，內涵720個bytes，也就是Code:`5-8`在做的事情(記得善用browser內建的debugger和觀察network封包)
2. 接著，如果甚麼都不輸入，他會直接執行assemble_png這個function(也就是Code:`10-29`)，這個function簡單來說就是descrable這些bytes，可以想像他用封包傳送過來的是已經打亂的bytes，所以他現在要還原
3. 具體怎麼還原可以用debugger跟一下，反正他的做法簡單來說就是他的原圖檔中每一行做輪轉，就像下圖一樣，我只有截第一行，其他以此類推，所以我們要怎麼回推回去呢?只要寫過一點Misc/修復png檔之類的題目的人應該對magic header不陌生，反正前面都長，那我們就直接回推到最一開始的狀態就好
![](https://hackmd.io/_uploads/rJ37YsIe6.png)
4. 而輪轉的次數介於0-9之間，所以可以像[^pico-java-script-kiddle-wp]的作法一樣，用看的，或是用[^pico-reverse-java-script-kiddle-wp-walkthru]的script(當然我有做一點修改，見註一)，而我們輸入的key長度是16，就是代表16行各自需要輪轉幾次回來，這樣我們就可以拿到一張正常的圖片
![](https://hackmd.io/_uploads/Sk3wHi8ep.png)
5. 問題：如果看其他人的WP，因為圖片不一樣，所以當然scramble bytes也會不一樣，但其他人好像沒有多個候選key的問題，也就是同一行中，在`0-9`的區間有超過一個一樣的bytes，這樣到底怎麼知道要輪轉幾次才會回到一開始的地方，比方說0x08這一行，應該要是0x00，但有多達三個0x00，這樣要怎麼知道輪轉2/3/4次?我自己的做法會是直接當成一個候選的key然後都try try看，以這個例子來說，就會多達3\*4\*3=36種組合
![](https://hackmd.io/_uploads/rk9K_sUea.png)

註一: 修改的地方在於前面的known_bytes，因為Walkthru的作法是前8bytes對照png前面的magic header，而後8bytes就看最後面的IEND block，但因為Code:`24-26`的while loop會把多餘的padding截掉，也就是說圖檔的bytes length不會和一開始打亂後的一樣，這樣在輪轉讓就沒辦法alignment，所以我就直接參考[^pico-java-script-kiddle-wp]的作法，也就是全部16bytes都依照第一列的16bytes當作對照，也就是`[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52]`，而這樣的結果就是有多達36種組合會出現，所以我才會寫一個自動填入候選key的script，這樣省了一點時間
## Exploit - Reverse / Javascript / Misc
:::spoiler 把bytes寫入圖片檔中
```python
ct = [87, 130, 78, 188, 0, 84, 26, 157, 143, 239, 249, 82, 248, 212, 239, 82, 195, 80, 1, 207, 13, 6, 1, 0, 119, 243, 73, 193, 78, 36, 133, 108, 85, 0, 0, 14, 0, 186, 68, 0, 0, 222, 0, 243, 0, 24, 174, 163, 126, 0, 133, 252, 137, 177, 121, 10, 0, 0, 0, 237, 73, 63, 0, 100, 96, 20, 3, 224, 59, 171, 16, 114, 0, 0, 0, 69, 0, 68, 68, 147, 137, 179, 110, 112, 74, 121, 238, 65, 1, 0, 156, 0, 155, 0, 95, 120, 0, 233, 226, 40, 78, 194, 248, 44, 84, 0, 208, 13, 41, 72, 138, 59, 164, 98, 71, 0, 209, 0, 99, 176, 97, 120, 202, 0, 135, 192, 54, 101, 64, 252, 81, 71, 205, 10, 243, 133, 30, 22, 125, 237, 3, 93, 90, 42, 73, 221, 25, 114, 243, 0, 116, 22, 4, 3, 59, 75, 188, 119, 169, 221, 161, 184, 178, 2, 73, 73, 231, 45, 14, 99, 102, 153, 166, 178, 206, 54, 127, 84, 240, 191, 220, 10, 163, 81, 64, 206, 128, 132, 102, 197, 72, 127, 239, 253, 78, 93, 8, 22, 239, 207, 146, 111, 143, 239, 27, 243, 28, 0, 173, 159, 196, 48, 247, 28, 84, 98, 63, 52, 171, 214, 214, 26, 233, 254, 65, 106, 111, 59, 73, 255, 148, 111, 103, 91, 20, 206, 222, 70, 252, 199, 161, 124, 245, 188, 102, 81, 159, 119, 174, 51, 190, 243, 55, 243, 156, 249, 124, 125, 2, 143, 191, 27, 119, 139, 126, 88, 18, 247, 171, 227, 72, 66, 54, 251, 0, 80, 171, 146, 113, 173, 4, 79, 211, 216, 214, 122, 119, 115, 225, 45, 24, 54, 44, 76, 43, 253, 5, 235, 104, 248, 96, 8, 229, 200, 75, 64, 233, 217, 23, 87, 40, 254, 187, 107, 181, 200, 181, 233, 181, 81, 231, 171, 165, 82, 254, 196, 239, 51, 43, 114, 170, 73, 249, 50, 114, 201, 138, 64, 11, 203, 155, 192, 249, 226, 35, 188, 156, 223, 40, 217, 67, 75, 100, 45, 93, 102, 169, 13, 34, 197, 80, 175, 210, 128, 137, 201, 167, 45, 140, 82, 171, 56, 212, 17, 126, 113, 139, 229, 127, 223, 181, 15, 0, 116, 221, 186, 219, 230, 56, 233, 31, 15, 249, 74, 119, 152, 44, 41, 226, 60, 35, 253, 172, 97, 32, 137, 233, 165, 35, 181, 104, 80, 217, 56, 186, 205, 212, 15, 64, 81, 230, 230, 153, 62, 251, 251, 47, 151, 141, 108, 32, 25, 65, 11, 253, 119, 201, 147, 243, 11, 31, 247, 233, 54, 126, 217, 136, 141, 191, 226, 137, 213, 131, 239, 100, 145, 151, 150, 119, 124, 159, 203, 190, 63, 18, 170, 210, 175, 122, 223, 223, 114, 124, 59, 93, 245, 177, 100, 15, 57, 63, 239, 165, 144, 13, 149, 32, 198, 39, 52, 53, 113, 97, 91, 186, 76, 91, 74, 207, 133, 208, 0, 245, 241, 245, 73, 122, 193, 223, 159, 82, 175, 241, 159, 231, 205, 24, 92, 75, 11, 247, 77, 55, 170, 7, 95, 127, 143, 96, 207, 242, 142, 153, 226, 242, 93, 163, 110, 185, 26, 188, 4, 178, 102, 159, 97, 53, 58, 186, 172, 239, 6, 78, 215, 65, 156, 90, 150, 112, 205, 73, 76, 149, 163, 159, 242, 45, 147, 16, 210, 49, 254, 82, 126, 200, 30, 62, 190, 230, 2, 86, 171, 181, 197, 185, 132, 170, 153, 82, 191, 154, 235, 147, 55, 57, 92, 252, 48, 207, 118, 191, 170, 253, 53, 127, 94, 143, 122, 230, 254, 154, 151, 186, 55, 160, 132, 126, 57, 183, 217, 129, 181, 95, 255, 35, 223, 50, 70, 77, 107, 100, 203, 17, 61, 163, 17, 227, 147, 182, 184, 79, 126, 239, 28, 115, 159, 254, 111, 90, 250, 14, 206, 185, 137, 187, 141, 231, 211, 241, 249, 39, 99, 131, 95, 210, 50, 147, 241, 95, 127, 103, 239, 113, 165, 223, 164, 245, 35, 231, 132, 166, 220, 241, 207, 67, 178, 148, 29, 156, 94, 194, 74, 222, 110, 0, 243, 107, 158, 173, 214, 210, 249, 84, 66, 107, 40, 0, 203, 138, 164, 0, 241, 9, 109, 147, 207, 85, 29, 204, 0]

f = open('./PicoCTF/Web/Java Script Kiddie/cipher_flag.png', 'wb')
pt = b''
count = 0
for i in range(len(ct)):
    pt += bytes([ct[i]])
f.write(pt)
f.close()
```
:::

:::spoiler 找key
```python=
ct = [87, 130, 78, 188, 0, 84, 26, 157, 143, 239, 249, 82, 248, 212, 239, 82, 195, 80, 1, 207, 13, 6, 1, 0, 119, 243, 73, 193, 78, 36, 133, 108, 85, 0, 0, 14, 0, 186, 68, 0, 0, 222, 0, 243, 0, 24, 174, 163, 126, 0, 133, 252, 137, 177, 121, 10, 0, 0, 0, 237, 73, 63, 0, 100, 96, 20, 3, 224, 59, 171, 16, 114, 0, 0, 0, 69, 0, 68, 68, 147, 137, 179, 110, 112, 74, 121, 238, 65, 1, 0, 156, 0, 155, 0, 95, 120, 0, 233, 226, 40, 78, 194, 248, 44, 84, 0, 208, 13, 41, 72, 138, 59, 164, 98, 71, 0, 209, 0, 99, 176, 97, 120, 202, 0, 135, 192, 54, 101, 64, 252, 81, 71, 205, 10, 243, 133, 30, 22, 125, 237, 3, 93, 90, 42, 73, 221, 25, 114, 243, 0, 116, 22, 4, 3, 59, 75, 188, 119, 169, 221, 161, 184, 178, 2, 73, 73, 231, 45, 14, 99, 102, 153, 166, 178, 206, 54, 127, 84, 240, 191, 220, 10, 163, 81, 64, 206, 128, 132, 102, 197, 72, 127, 239, 253, 78, 93, 8, 22, 239, 207, 146, 111, 143, 239, 27, 243, 28, 0, 173, 159, 196, 48, 247, 28, 84, 98, 63, 52, 171, 214, 214, 26, 233, 254, 65, 106, 111, 59, 73, 255, 148, 111, 103, 91, 20, 206, 222, 70, 252, 199, 161, 124, 245, 188, 102, 81, 159, 119, 174, 51, 190, 243, 55, 243, 156, 249, 124, 125, 2, 143, 191, 27, 119, 139, 126, 88, 18, 247, 171, 227, 72, 66, 54, 251, 0, 80, 171, 146, 113, 173, 4, 79, 211, 216, 214, 122, 119, 115, 225, 45, 24, 54, 44, 76, 43, 253, 5, 235, 104, 248, 96, 8, 229, 200, 75, 64, 233, 217, 23, 87, 40, 254, 187, 107, 181, 200, 181, 233, 181, 81, 231, 171, 165, 82, 254, 196, 239, 51, 43, 114, 170, 73, 249, 50, 114, 201, 138, 64, 11, 203, 155, 192, 249, 226, 35, 188, 156, 223, 40, 217, 67, 75, 100, 45, 93, 102, 169, 13, 34, 197, 80, 175, 210, 128, 137, 201, 167, 45, 140, 82, 171, 56, 212, 17, 126, 113, 139, 229, 127, 223, 181, 15, 0, 116, 221, 186, 219, 230, 56, 233, 31, 15, 249, 74, 119, 152, 44, 41, 226, 60, 35, 253, 172, 97, 32, 137, 233, 165, 35, 181, 104, 80, 217, 56, 186, 205, 212, 15, 64, 81, 230, 230, 153, 62, 251, 251, 47, 151, 141, 108, 32, 25, 65, 11, 253, 119, 201, 147, 243, 11, 31, 247, 233, 54, 126, 217, 136, 141, 191, 226, 137, 213, 131, 239, 100, 145, 151, 150, 119, 124, 159, 203, 190, 63, 18, 170, 210, 175, 122, 223, 223, 114, 124, 59, 93, 245, 177, 100, 15, 57, 63, 239, 165, 144, 13, 149, 32, 198, 39, 52, 53, 113, 97, 91, 186, 76, 91, 74, 207, 133, 208, 0, 245, 241, 245, 73, 122, 193, 223, 159, 82, 175, 241, 159, 231, 205, 24, 92, 75, 11, 247, 77, 55, 170, 7, 95, 127, 143, 96, 207, 242, 142, 153, 226, 242, 93, 163, 110, 185, 26, 188, 4, 178, 102, 159, 97, 53, 58, 186, 172, 239, 6, 78, 215, 65, 156, 90, 150, 112, 205, 73, 76, 149, 163, 159, 242, 45, 147, 16, 210, 49, 254, 82, 126, 200, 30, 62, 190, 230, 2, 86, 171, 181, 197, 185, 132, 170, 153, 82, 191, 154, 235, 147, 55, 57, 92, 252, 48, 207, 118, 191, 170, 253, 53, 127, 94, 143, 122, 230, 254, 154, 151, 186, 55, 160, 132, 126, 57, 183, 217, 129, 181, 95, 255, 35, 223, 50, 70, 77, 107, 100, 203, 17, 61, 163, 17, 227, 147, 182, 184, 79, 126, 239, 28, 115, 159, 254, 111, 90, 250, 14, 206, 185, 137, 187, 141, 231, 211, 241, 249, 39, 99, 131, 95, 210, 50, 147, 241, 95, 127, 103, 239, 113, 165, 223, 164, 245, 35, 231, 132, 166, 220, 241, 207, 67, 178, 148, 29, 156, 94, 194, 74, 222, 110, 0, 243, 107, 158, 173, 214, 210, 249, 84, 66, 107, 40, 0, 203, 138, 164, 0, 241, 9, 109, 147, 207, 85, 29, 204, 0]
known_bytes = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52]

possible_key_values = []

for key_pos in range(16):
    known_byte = known_bytes[key_pos]

    current_block = 0

    key_possiblities = []
    possible_key_values.append(key_possiblities)

    target_indices = [index for index, b in enumerate(ct) if b == known_byte]

    for possible_key_value in range(10):
        byte_index = (((current_block + possible_key_value) * 16) % len(ct)) + key_pos
        if byte_index in target_indices:
            key_possiblities.append(possible_key_value)
        
print(possible_key_values)
```
:::

:::spoiler 自動填入可能的key
```python!
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from tqdm import trange

PATH = './chromedriver.exe'
driver = webdriver.Chrome()
driver.get('https://jupiter.challenges.picoctf.org/problem/17205/?#')


img = driver.find_element(By.ID, "Area")

ct = '51081803ghi63640'
pt_guess = []
for i in range(10):
    for j in range(10):
        pt_guess.append(ct.replace('g', hex(j)[2:]).replace('h', hex(i)[2:]))

for i in trange(len(pt_guess)):
    element = driver.find_element(By.ID, 'user_in')
    click = driver.find_element(By.XPATH, "/html/body/center/form/input[2]")
    element.send_keys(pt_guess[i])
    click.click()
    time.sleep(0.01)
    driver.refresh()
```
:::

首先，可以先用script 1，把資料寫到一個file中，然後用看的或是用script 2直接找，輸出如下
```bash
[[5], [1], [0], [8], [1], [8], [0], [3], [2, 3, 4], [3, 4, 5, 6], [2, 3, 4], [6], [3], [6], [4], [0]]
```
有了候選的key後就可以用script 3自動填入，另外要提醒的是要先把key變成`51081803ghi63640`，也就是把重複的地方變成ghi，然後填入重複的次數

---
Key: `5108180345363640`
QRCode:
![](https://hackmd.io/_uploads/r16cCqIxT.png)
Flag: `picoCTF{066cad9e69c5c7e5d2784185c0feb30b}`
## Reference
[^pico-java-script-kiddle-wp]:[ [網頁漏洞] Javascript - 修正圖檔 ](https://ithelp.ithome.com.tw/articles/10250802)
[^pico-reverse-java-script-kiddle-wp-walkthru]:[ PicoCTF Walkthru [101] - Java Script Kiddie (JavaScript debugging/reversing) ](https://youtu.be/ltLA-q3FLvI?si=_jtjyl6mwsT0mnVV)