PWN

mybirthday

author: pakkunandy, 50 points

Đề cho ta một file elf 32bit, đầu tiên ta chạy thử:

Chương trình yêu cầu chúng ta nhập vào và xuất ra thời gian theo hệ thống.

Ta dissassembly hàm main của chương trình:

   0x080492bb <+60>:	add    esp,0x10
   0x080492be <+63>:	sub    esp,0x4
   0x080492c1 <+66>:	push   0x1e
   0x080492c3 <+68>:	lea    eax,[ebp-0x24]
   0x080492c6 <+71>:	push   eax
   0x080492c7 <+72>:	push   0x0
   0x080492c9 <+74>:	call   0x80490a0 <read@plt>
   0x080492ce <+79>:	add    esp,0x10
-->0x080492d1 <+82>:	cmp    DWORD PTR [ebp-0xc],0xcabbfeff
   0x080492d8 <+89>:	jne    0x80492ee <main+111>
   0x080492da <+91>:	sub    esp,0xc
   0x080492dd <+94>:	lea    eax,[ebx-0x1fe1]
   0x080492e3 <+100>:	push   eax
   0x080492e4 <+101>:	call   0x8049254 <run_cmd>
   0x080492e9 <+106>:	add    esp,0x10
   0x080492ec <+109>:	jmp    0x8049300 <main+129>
   0x080492ee <+111>:	sub    esp,0xc
   0x080492f1 <+114>:	lea    eax,[ebx-0x1fd7]
   0x080492f7 <+120>:	push   eax
   0x080492f8 <+121>:	call   0x8049254 <run_cmd>

Chương trình sẽ đọc vào một chuỗi có độ dài 0x1e và đọc vào [ebp-0x24], sau đó kiểm tra [ebp-0xc] có bằng 0xcabbfeff hay không.

Boom! Ez bufferoverflow!

Flag: HCMUS-CTF{Just_A_Warm_Up_Pwn}

bank1

author: xikhud, 50 points

Không hề có file binary và chỉ có server để connect!!!???

Vì giá trị của challenge chỉ nằm ở con số 50 nên mình cũng đoán được bài này sẽ như thế nào :D

Mạnh dạn in thẳng vài trăm ký tự vào server =» Boom =» Flag

Thực ra mình nghĩ bài này mục đích chỉ là overflow được con số balance của tài khoản, vì thử phát trúng luôn nên mình cũng không suy nghĩ thêm!

python3 -c "print('A'*100)" | nc server.quenmattiuroi portgiday

Flag: HCMUS-CTF{that_was_easy_xd}

bank2

author: xikhud, 100 points

Đọc assembly của bài này ta thấy chương trình nhận vào input của người dùng. Và hàm nhận vào input là hàm gets().

Vâng, old buddy gets, một hàm tuyệt vời của C được mô tả trên man page như sau:

DESCRIPTION

   > _ Never use this function. _
   > gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with a null byte ('\0').  No check for buffer overrun is performed.

Nhiệm vụ của ta là overflow được [ebp-0xc] bằng với giá trị 0x66a44:

   0x08048554 <+35>:	call   0x8048390 <printf@plt>
   0x08048559 <+40>:	add    esp,0x10
   0x0804855c <+43>:	sub    esp,0xc
   0x0804855f <+46>:	lea    eax,[ebp-0x4c]
   0x08048562 <+49>:	push   eax
   0x08048563 <+50>:	call   0x80483a0 <gets@plt>
   0x08048568 <+55>:	add    esp,0x10
   0x0804856b <+58>:	sub    esp,0x8
   0x0804856e <+61>:	push   DWORD PTR [ebp-0xc]
   0x08048571 <+64>:	lea    eax,[ebx-0x1964]
   0x08048577 <+70>:	push   eax
   0x08048578 <+71>:	call   0x8048390 <printf@plt>
   0x0804857d <+76>:	add    esp,0x10
-->0x08048580 <+79>:	cmp    DWORD PTR [ebp-0xc],0x66a44
   0x08048587 <+86>:	jne    0x804858e <Register+93>
   0x08048589 <+88>:	call   0x8048506 <getFlag>
   0x0804858e <+93>:	nop
   0x0804858f <+94>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08048592 <+97>:	leave  
   0x08048593 <+98>:	ret    

Flag: HCMUS-CTF{little_endian_is_fun}

bank3

author: xikhud, 100 points

Làm gì làm, đọc assembly trước đã :P

   0x08048531 <+0>:		push   ebp
   0x08048532 <+1>:		mov    ebp,esp
   0x08048534 <+3>:		push   ebx
   0x08048535 <+4>:		sub    esp,0x54
   0x08048538 <+7>:		call   0x8048440 <__x86.get_pc_thunk.bx>
   0x0804853d <+12>:	add    ebx,0x1ac3
   0x08048543 <+18>:	mov    DWORD PTR [ebp-0xc],0x0
   0x0804854a <+25>:	sub    esp,0xc
   0x0804854d <+28>:	lea    eax,[ebx-0x1993]
   0x08048553 <+34>:	push   eax
   0x08048554 <+35>:	call   0x8048390 <printf@plt>
   0x08048559 <+40>:	add    esp,0x10
   0x0804855c <+43>:	sub    esp,0xc
   0x0804855f <+46>:	lea    eax,[ebp-0x4c]
   0x08048562 <+49>:	push   eax
   0x08048563 <+50>:	call   0x80483a0 <gets@plt>
   0x08048568 <+55>:	add    esp,0x10
   0x0804856b <+58>:	sub    esp,0x8
   0x0804856e <+61>:	push   DWORD PTR [ebp-0xc]
   0x08048571 <+64>:	lea    eax,[ebx-0x1974]
   0x08048577 <+70>:	push   eax
   0x08048578 <+71>:	call   0x8048390 <printf@plt>
   0x0804857d <+76>:	add    esp,0x10
   0x08048580 <+79>:	nop
   0x08048581 <+80>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08048584 <+83>:	leave  
   0x08048585 <+84>:	ret    

Hmm, lần này ta thấy chương trình không hề kiểm tra giá trị gì để ta lấy được flag cả, nhưng để ý những hàm của chương trình này đi:

db-peda$ info functions
All defined functions:

File ./src/bank3.c:
9:	void Register();
4:	void getFlag();
18:	int main(int, char **);

Một hàm getFlag() ở ngay đây, và lần này ta chỉ việc overflow giá trị trả về với địa chỉ của hàm:

gdb-peda$ pdisas getFlag
Dump of assembler code for function getFlag:
-->0x08048506 <+0>:		push   ebp <-- "I'm here, guyss"
   0x08048507 <+1>:		mov    ebp,esp
   0x08048509 <+3>:		push   ebx
   0x0804850a <+4>:		sub    esp,0x4
   0x0804850d <+7>:		call   0x80485dc <__x86.get_pc_thunk.ax>
   0x08048512 <+12>:	add    eax,0x1aee
   0x08048517 <+17>:	sub    esp,0xc
   0x0804851a <+20>:	lea    edx,[eax-0x19a0]
   0x08048520 <+26>:	push   edx
   0x08048521 <+27>:	mov    ebx,eax
   0x08048523 <+29>:	call   0x80483c0 <system@plt>
   0x08048528 <+34>:	add    esp,0x10
   0x0804852b <+37>:	nop
   0x0804852c <+38>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x0804852f <+41>:	leave  
   0x08048530 <+42>:	ret    
End of assembler dump.

Flag: HCMUS-CTF{overwrite_all_the_things}

secretweapon

author: pakkunandy, 100 points

Tương tự bank3, bài này cũng là bài toán overflow return address.

Tuy nhiên, ở bài này ta thấy ASLR được bật, và tác giả đã đủ nhân hậu để leak cho ta một địa chỉ.

Nhiệm vụ của ta bây giờ chỉ là tính địa chỉ tương đối giữa địa chỉ được leak ra và địa chỉ của hàm mục tiêu (hàm arsenal()).

Xin lỗi bạn đọc, nhưng tôi đã quá lười để viết writeup ._.

Flag: HCMUS-CTF{you_know_how_to_compute_location}

bank4

author: xikhud, 200 points

Ok, bài 200 điểm đầu tiên. Chạy binary ta thấy vẫn như các bài trước không có gì mới mẻ, ta nhảy vào xem luôn assembly. Lần này mình decompile code để cho write up có vẻ đẹp và sang hơn một chút :D

void Register(void)

{
  char name [64];
  int balance;
  
  printf("[+] Please enter your name: ");
  gets(name);
  printf("[+] Thanks for the registration, your balance is %d.\n",0);
  return;
}

void getFlag(void)

{
  if ((o1 == 0) || (o2 == 0)) {
    system("echo \"hcmasd-cft{nah_nah_nah_not_today}\"");
  }
  else {
    system("cat flag.txt"); //notice me
  }
  return;
}

Sau khi xem xét được 2 hàm thì ta thấy mục đích vẫn là đưa chương trình trở về hàm getFlag(). Tuy nhiên, để ý thì hàm này kiểm tra xem 2 biến o1o2 có bằng 0 không, và 2 biến này được lưu trên vùng nhớ của chương trình.

Hmm?!?

Nhưng ta đâu nhất thiết phải trở về đầu hàm getFlag() đâu nhỉ? Nếu chúng ta ăn gian bớt thì sao??

Tuy nhiên, đoạn code ở trên không hề phản ánh rõ cái gì đang ở bên dưới máy tính, và ta dĩ nhiên không thể nhảy tùy tiện về dòng code system(“cat flag.txt”); được

   0x08048906 <+0>:		push   ebp
   0x08048907 <+1>:		mov    ebp,esp
   0x08048909 <+3>:		push   ebx
   0x0804890a <+4>:		sub    esp,0x4
 $ 0x0804890d <+7>:		call   0x8048a06 <__x86.get_pc_thunk.ax>
 $ 0x08048912 <+12>:	add    eax,0x916ee
   0x08048917 <+17>:	mov    edx,DWORD PTR [eax+0x133c]
   0x0804891d <+23>:	test   edx,edx
   0x0804891f <+25>:	je     0x8048941 <getFlag+59>
   0x08048921 <+27>:	mov    edx,DWORD PTR [eax+0x1340]
   0x08048927 <+33>:	test   edx,edx
   0x08048929 <+35>:	je     0x8048941 <getFlag+59>
 * 0x0804892b <+37>:	sub    esp,0xc
 * 0x0804892e <+40>:	lea    edx,[eax-0x2d838] 		-->'cat flag.txt'
 * 0x08048934 <+46>:	push   edx
 * 0x08048935 <+47>:	mov    ebx,eax
 * 0x08048937 <+49>:	call   0x804fc20 <system>
   0x0804893c <+54>:	add    esp,0x10
   0x0804893f <+57>:	jmp    0x8048955 <getFlag+79>
   0x08048941 <+59>:	sub    esp,0xc
   0x08048944 <+62>:	lea    edx,[eax-0x2d828]
   0x0804894a <+68>:	push   edx
   0x0804894b <+69>:	mov    ebx,eax
   0x0804894d <+71>:	call   0x804fc20 <system>
   0x08048952 <+76>:	add    esp,0x10
   0x08048955 <+79>:	nop
   0x08048956 <+80>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08048959 <+83>:	leave  
   0x0804895a <+84>:	ret    

Xem xét kĩ thì ta thấy hàm system nhận vào chỉ 1 tham số, chính là chuỗi ‘cat flag.txt’, tuy nhiên chuỗi này được lấy bằng cách tính vị trí tương đối từ thanh ghi $eax.

Nếu lần lên trên ta sẽ thấy $eax đã được gán bằng giá trị 0x0848912 sau lời gọi hàm <__x86.get_pc_thunk.ax> (hàm này trả về giá trị của thanh ghi pc, chính là địa chỉ lệnh cần thực hiện tiếp theo), và sau đó được tăng thêm 0x916ee đơn vị. Vậy thanh ghi $eax bây giờ có giá trị là 0x080da000.

Vậy làm sao để gán giá trị đó cho eax trước khi trở về?

ROPgadget hân hạnh tài trợ chương trình này! Tìm một gadget “pop eax; ret” hay tương tự và push thêm giá trị $eax mong muốn, sau đó trở về địa chỉ ngay trước khi lời gọi system được thực hiện và ta được flag!!

Flag: HCMUS-CTF{trungdeptrai}

bank5

author: xikhud, 200 points

Tiếp tục là một bài ROP, và lần này không có bất kỳ hàm getFlag() nào T.T

Tuy nhiênnnnn, ta chỉ cần ROP là đủ để chiếm shell của server, và lần này ta sẽ gọi thẳng syscall để boom luôn cái server.

Đơn giản chỉ cần tìm đủ những gadget cần thiết để chỉnh các thanh ghi và gọi hàm theo đúng như trong sách giáo khoa là được.

Lần này mình xin phép nhường bạn đọc và không giải thích gì thêm ._.

Hint: ở đây ta có thể gọi syscall execve(‘/bin/sh’) và tùy biến các giá trị thanh ghi cho đúng với yêu cầu

Flag: HHCMUS-CTF{rop_and_shellcode}

bank6

author: xikhud, 200 points

Lần này là một bài giới hạn input nhận vào (bye bye gets, welcome to scanf). Việc giới hạn này làm ta không thể overflow được giá trị trả về, nhưng ta lại được một stack khá lớn (1024 ký tự).

Chạy thử chương trình thì ta thấy như sau:

[+] Welcome
[+] Here is a gift: 0xff86401c
[+] Please enter your name:

Một địa chỉ???

Checksec kiểm tra thấy NX không bật. Và kiểm tra lại thì ta thấy stack is executable. Đương nhiên cái địa chỉ kia sẽ có vai trò trong việc xác định stack ở đâu.

Tuy ta không overflow được return address, nhưng ta có thể khiến nó trở về được địa chỉ ta mong muốn. O.O

Nhận thấy hàm này return đến 2 lần, và lần đầu không được thì ta xem lần 2 :>

Mỗi lời gọi trở về hàm đều kết thúc bằng “leave; ret” và xem xét flow của chương trình, ta nhận thấy nếu ta overflow tới 4 bytes của ebp, ta thật sự có thể khiến nó trở về đâu đó trên stack của ta.

Quan sát trên stack ta thấy giá trị return address luôn có dạng 0x??????00 và 3 bytes đầu của nó khá gần với địa chỉ của stack.

Tới đây ta có thể bruteforce hoặc tính toán một cách “gần đúng nhất” và chờ kết quả thôi.

Khá hên cho mình là bem ngay lần đầu Ლ(=ↀωↀ=)ლ

Flag: HCMUS-CTF{0ff_by_on3}

MISC

dodge

50 points

Sau khi ssh vào server, ta được một restricted shell.

Mình đã nghịch thử một ít lệnh và thấy cái shell này chỉ cho ta dùng một số lệnh nhất định như ls, echo, và trong đó không có lệnh cat.

Tuy nhiên, ta vẫn có thể đọc file mà không cần lệnh cat:

echo `<flag.txt`

Flag: HCMUS-CTF{You_know_some_command_line_stuff}

strangerthing

50 points

Bài này flag được chia làm 3 mảnh và ta cần đọc được chúng để ghép lại với nhau.

cat * –> ta đọc được 2 mảnh đầu tiên (mình đã né việc đọc flag2.txt với dấu - ở trong filename (。•̀ᴗ-)✧ )

Mảnh cuối cùng của flag được giấu trong thư mục secret với rất nhiều thư mục con, ta dùng lệnh find để tìm flag3 khi biết trong nội dung của nó phải có ký tự ‘}’

grep -Ril “}” /

Flag: HCMUS-CTF{this_is_used_to_test_linux_command_line}

escapeme

_100 points

Một chiếc shell có user là ctf và một file flag.txt chỉ có root mới được đọc.

Có thể thấy mục tiêu của challenge này là ta phải chiếm quyền root. Sau khi đọc một số writeup trên mạng, mình thấy lệnh sudo -l có thể liệt kê các lệnh mà rootuser đều có quyền.

Và ta thấy python3 (つ✧ω✧)つ

Mình thử chạy một lệnh python3 đơn giản để lấy shell và chạy nó bằng sudo:

sudo python3 -c "import os; os.system('/bin/bash')"

Flag: HCMUS-CTF{privilege_escalation_is_fun!!!}

FORENSICS

saveme

50 points

Bài này cho ta một file raw, check file thì không nhận diện được đây là file gì cả.

Mình mở hexdump của file lên và nhận thấy các byte đầu là signature của file png nhưng đã bị đảo.

Viết code python để đảo các byte này cho đúng:

file = open('saveme','rb')
file2 = open('output','wb')
byte = file.read(1)
byte2 = file.read(1)
while byte:
    while byte2:
        file2.write(byte2)
        file2.write(byte)
        byte = file.read(1)
        byte2 = file.read(1)

Sau khi có được file output, mình dùng binwalk để extract các file ra và lấy được flag.

Flag: HCMUS-CTF{You_Know_How_To_Manipulate_Images_1324587}

REVERSE

faded

100 points

Dựa vào gợi ý của đề thì mình biết được file elf này được build từ Pyinstaller. Và mình dùng pyi-archive_viewer để kiểm tra file. Sau đó mình extract hàm chính của file này ra và grep được flag.

Flag: HCMUS-CTF{Python_is_fun_somehow}