Challenge ini merupakan salah satu challenge yang cukup sulit menurut saya, bahkan mungkin sangat sulit sejauh saya mengikuti CTFR, mungkin karena ketidak tahuan saya dalam membaca petunjuk dan maksud dari challengenya, deskripsinya juga sangat sederhana, tidak banyak yang bisa diambil sebagai petunjuk, satu-satunya petunjuk hanya merujuk pada gambar yang diberikan.
Download : https://mega.nz/#!VthwnaSB!ajJ2S4MfM4UPPdoqaQXyAuGtnynRpyQWsWYPO8F3s_k
setelah didownload, terdapat 2 buah file challenge_pygame.png
dan pygame_chall.py
, isi dari challenge_pygame.png
seperti berikut
berdasarkan keterangan dari challengenya, gambar diatas dihasilkan oleh script python challenge_pygame.py
import pygame, random, string, time
from Crypto.Util.number import inverse
from pygame.locals import *
from pygame.surface import Surface
pygame.init()
width, height = 1280, 720
screen = pygame.display.set_mode((width, height))
nonvertical = 3
def genChar():
c = string.digits + string.ascii_letters
return c[random.randint(0, len(c) - 1)]
def genNumber(min, max):
res = []
for x in range(min, max + 1):
if x > 1:
for i in range(2, x):
if (x % i) == 0:
break
else:
res.append(x)
return res
def draw(x, y, color, size):
global screen
s = Surface(size)
s.fill(color)
screen.blit(s, (x, y))
c = 0
def getObjColor(x):
stor = ord(x)
x = ord(x)
if x >= 65 and x <= 90:
x += int(time.strftime("%S"))
elif x >= 97 and x <= 122:
x += int(time.strftime("%M"))
else:
x += int(time.strftime("%H"))
num = genNumber(stor - 64, stor)
v1 = abs(num[4] - c)
v2 = abs(num[6] + c)
v3 = (v1 * v2) % 255
return (v1 + v2, v3 ^ x, v3)
flag = "TEST"
len_flag = len(flag)
max_height = int(round(height / len_flag))
usedheight = max_height
while True:
screen.fill(0)
usedheight = 0
for ch in flag:
target = ch
for x in range(0, width, width / max_height):
draw(x, usedheight, getObjColor(target), (width / max_height, max_height))
c += 1
c = 0
usedheight += max_height
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
Awalnya saya berpikir ini mungkin manupulasi pixel warna pada sebuah gambar, tetapi ternyata saya salah, setelah melihat-lihat isi dari code yang diberikan saya sadar jika warna pada gambar yang diberikan ditentukan oleh panjang Flagnya yaituflag = "TEST"
dan berdasarkan waktu pada saat proses berjalan yang ada pada fungsi def getObjColor(x)
Misalnya saja pada contoh kode diatas, flag='Test'
akan digunakan sebagai penentu seberapa banyak looping yang akan diproses untuk membentuk atau menggambar sebuah warna dengan panjang dan tinggi tertentu, bisa kita lihat pada bagian kode
draw(x, usedheight, getObjColor(target), (width / max_height, max_height))
penggalan code tersebut memanggil fungsi
def draw(x, y, color, size):
global screen
s = Surface(size)
s.fill(color)
screen.blit(s, (x, y))
dari sini kita bisa melihat jika setiap perulangan sepanjang flag
akan membuat sebuah object
atau fill
dengan posisi x
dan y
dan warna
serta ukuranya
, dimana ukuran dari object tersebut ditentukan dari max_height = int(round(height / len_flag))
, serta usedheight += max_height
yang akan dirubah nilainya sepanjang perulangan, sederhanya jika kita terjemahkan dengan value flag="TEST"
mungkin kira-kira akan menjadi seperti ini
len_flag = 4
max_height = int(round(720 / 4)) = 180
usedheight = max_height
for x in range(0,720,180):
draw(x, 180, getObjColor('T'), (1, 180))
jika kita breakdown, len_flag = 4
dimana flag
hanya berupa string 'TEST'
berjumlah 4 karakter
, kemudian max_height=180
yang berasal dari pembulatan height/4
, jika kita berasumsi pada proses perulangan for x in range(0,720,180)
maka perulangan akan di awali dengan 0
dan kemudian kelipatan 180
hingga pada batas 720
nilai x
jika diulang sebanyak 4
kali akan menjadi 0, 180, 360, 540
, dari sini kita bisa mengambil kesimpulan pada perulangan pertama object fill
dengan warna('T')
akan dibuat pada koordinat x=0, y=180
dengan ukuran 1x180 pixel
, sejauh ini informasi ini belum cukup membantu, karena setelah saya coba tidak jalan.
pygame 2.0.1 (SDL 2.0.14, Python 2.7.17)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File ".\pygame_chal.py", line 64, in <module>
draw(x, usedheight, getObjColor(target), (width / max_height, max_height))
File ".\pygame_chal.py", line 31, in draw
s.fill(color)
ValueError: invalid color argument
setelah saya telusuri ternyata proses pembuatan object
pada proses draw()
dipengaruhi oleh pangjang flag
, sehingga pada proses def getObjColor(x)
menghasilkan warna yang tidak valid alias lebih besar dari 255
(861, 0, (252, 219, 170), (7, 180))
(868, 0, (254, 217, 168), (7, 180))
(875, 0, (256, 217, 168), (7, 180)) # (R=256, G=217, B=168) pixel Red lebih besar dari 255
Jadi harus mencari tau dulu berapa panjang flag
nya, cukup lama saya mantengin darimana bisa dapat panjang flagnya, setelah bolak balik melihat gambar yang diberikan dan mencoba melihat-lihat isi pixelnya, ternyata oh ternyata panjang flagnya ada didalam gambar yang diberikan, bisa teman-teman lihat pada gambar yang diberikan, jika diperhatikan baik-baik dan menghitung jumlah warna pada gambar dari posisi atas kebawah semua berjumlah 64
baris warna, dengan meberikan nilai flag = 'A' * 64
dan menjalankan programnya hasilnya
hasilnya tidak error, dan pygame
berjalan dengan semestinya, masalahnya warna yang dihasilkan tidak sesuai dengan capture
yang diberikan, selanjutnya harus mencari warna yang sesuai dan ukuran yang sesuai agar gambar yang dihasilkan sama dengan gambar yang diberikan, dari tahapan ini sudah jelas kalo kita harus mencari karakter by karakter yang bisa mebentuk warna yang sama, untuk itu kita coba dulu dengan karakter standar, diman kita tau bahwa format flagnya adalah CTFR{....}
jadi kita mencoba memberikan flag = 'CTFR{'+'A' * (64-5)
dan hasilnya adalah
dari sini kita berhasil mendapatkan titik yang sangat terang, kita berhasil mendapatkan object fill
yang sesuai walaupun warnya tidak sama tetapi format penempatan lebar dan tingginya sudah sama, sesuai string yang kita berikan 5 string
pertama yaitu CTFR{
membentuk 5 baris
object warna, langkah selanjutnya adalah mencari tau sisa dari flagnya dengan cara brute forcing
.
pada tahapan ini mungkin cara yang saya gunakan tidak efisien atau bukan cara yang seharusnya, jika teman-teman merasa ada caranya yang lebih simple bisa share di group whatsapp CTFR, saya disini melakukan brute forcing
warna yang ada pada gambar yang diberikan dan mencocokan karakter apa yang sesuai dengan warna tersebut, karena gambar yang diberikan adalah gambar hasil capture-an jadi harus menghitung dulu mulai dari mana pengambilan pixel
warna-nya.
sebelum melakukan brute forcing
, sebaiknya kita pahami dulu sedikit tentang fungsi def getObjColor(x)
karena fungsi inilah yang bertanggung jawab dalam membuat warna dari gambarnya, jika diperhatikan dengan seksama hal yang paling penting ada pada bagian v3 = (v1 * v2) % 255
dan return (v1 + v2, v3 ^ x, v3)
, disini warna dibuat menggunakan xor
tentunya setelah beberapa proses sebelumnya, tetapi menurut saya bagian inilah yang paling penting.
jika seandainya kita memproses karakter C
maka proses yang terjadi pada fungsi ini adalah, karakter c
akan dirubah dulu menjadi bentuk ordinal yaitu ord('C') = 67
dan disimpan dalam variable stor
dan x
, kemudian pada bagian berikutnya, karena x=67
maka kondisi if x >= 65 and x <= 90
akan terpenuhi, sehingga x = x + 29
(asumsinya terjadi pada detik ke 29) dilanjutkan dengan proses num = genNumber(67- 64, 67)
jika kita merujuk ke fungsi genNumber(min,max)
maka num
akan bernilai [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67]
Dari hasil tersebut proses dilanjutkan dengan mendapatkan v1 = abs(num[4] - c)
dimana num[4] = 13
jadinya v1 = abs(13 - 0)
kita berasumsi jika perulangan ada pada 0
sehingga bisa kita simpulkan jika v1 = 13
, sama halnya berlaku dengan v2
, v2
akan bernilai v2 = abs(19 + 0)
atau sama dengan v2=19
dan v3 = (13* 19) % 255
sehingga v3 = 247
dengan begitu nilai return dari def getObjColor(x)
akan bernilai (13+19, 247 ^ 67, 247)
atau sama dengan karakter C
akan menghasilkan warna (32, 180, 247)
kembali ke point awal, karena operasinya menggunakan operator XOR
jadi kita bisa membalikan hasilnya untuk mencari tau nilai awal sebelum penjumlahan dengan mencari tau %S
%M
dan %H
waktu pemrosesan. disini saya mendapatkan waktu %S=38
%M=48
dan %S=8
dengan variable tersebut karakter C
mendapatkan warna dan posisi yang sama dengan gambar yang diberikan
sejauh ini semua kebutuhan untuk bruteforcing sudah terpenuhi, tinggal membuat script sederhana untuk melakukan tugas mencari karakter-karakter yang memiliki warna berdasarkan penelusuran sebelumnya.
dan jika hasilnya dimasukan dalam variable flag pada script yang diberikan, pygame akan menghasilkan gambar yang sama dengan gambar yang diberikan.
Flagnya
CTFR{wh3n_y0u_h4t3_p1x3l_but_th4t_w45_t45k_y0u_mu5t_t0_s0lv3_1t}