[SVATTT CTF 2013] Vết tích tại hiện trường (Web200)

Anonymous đã upload thành công một script backdoor của bọn chúng lên server ngân hàng Trung Ương với mục đích sẽ quay lại kiếm lợi sau này. Nhưng để truy xuất file, bạn cần phải biết password của chúng đã qui ước với nhau từ trước. Chỉ mất khoảng 10 phút, Chiến đã bypass được. Thế còn các bạn thì sao? :)
Challenge: Link
Hint: hint.txt

Chúng ta có một vùng nhập séc rét, và file hint.txt có nội dung xinh xinh:

Nội dung chính

Ta thấy rằng, yêu cầu của bài này là tìm một password thỏa mãn 2 điều kiện:

  1. Có chứa Anonymous.
  2. CRC32(password) = deadc0de.

Sau đó, (có lẽ là) flag sẽ được lấy từ database qua câu query:

Như vậy, nhiệm vụ của chúng ta, ngoài việc tìm password thỏa mãn 2 điều kiện vừa nói ở trên, còn có thêm một yếu tố nữa, đó là bypass được câu query SQL cuối cùng.

Với 2 nhiệm vụ đầu, công việc khá đơn giản, do giá trị CRC32 có thể được chỉnh sửa tùy ý bằng cách thay đổi 4 byte trong password. Một vài tool trên mạng cho phép chúng ta làm điều này, thay vì tự code, ví dụ như:

Giờ nếu ta tạo password ban đầu là:

Anonymousabcd

và dùng tool trên, kết quả cho ra:

Anonymous[q>_

Đây là một password khá đẹp, nó có chứa Anonymous, và CRC32 của nó đúng bằng deadc0de. Tuy nhiên khi submit, ta nhận thông báo sau:

Anonymous: Nice try! But You are doing it wrong :)

Ok, đây là điều chúng ta đã dự đoán từ trước, do vẫn chưa giải quyết được phần SQL.

Tuy vậy, việc inject câu query này xem chừng khá đơn giản, chúng ta sẽ thử với một password khá gia giáo và truyền thống:

Anonymous' OR 1=1--aaaa

Submit sau khi đã đưa nó qua forcecrc32.py, thông báo trả về xấu hơn một chút:

Bad input :)!

Vậy là có dính phải các ký tự bị cấm rồi too_sad

Thử thêm chút nữa, ta thấy các ký tự bị cấm dường như là: <space>, <comment> blah blah… tuy nhiên các từ khóa and, or, dấu nháy đơn () và dấu chấm phẩy (;) thì không bị. Hơ, thế cũng là đủ rồi emo_popo_look_down

Password cuối cùng mà ta lựa chọn sẽ là:

Anonymous'or'1'='1';aaaa

Makeup một chút với CRC32, ta được password xịn:

Anonymous'or'1'='1';nx9Dxx92

Submit:

Anonymous: Not bad! Here is your flag

SVATTT_crc_0r_n0t_CRC

emo_popo_misdoubt

[WarGameVN CTF] SecretKeeper (Web300)

Đây là nơi trao đổi bí mật giữa các tên trùm.

Source: ~

 

Rất cảm ơn BTC vì đã gìn giữ để cái URL nó sống đến tận bây giờ sweet_kiss

Đây là một bài mà hồi đó, mình nhìn nó với một sự ngưỡng mộ sâu sắc, và thậm chí, ngay cả khi có một vài member kiệt xuất trong team mình miên man bình luận về nó thì mình vẫn như một thằng lơ ngơ không hiểu gì.

Mình thích cái cảm giác lâu lâu quay lại một bài nào đó, một bài mà trước đây mình không làm được, thậm chí không biết nó hỏi cái gì, nó cần cái gì, rồi sau đó nó hét lên với mình: “Cờ của mày đây!”, cảm giác thật thanh thản và bình yên sexy_girl

Ngày còn bé (cụ thể là cấp 3), mình cực kỳ yếu tích phân, và dù cho đã cố lăn lộn với nó trong suốt 3 năm học, cộng với vài ngày sát kỳ thi ĐH, mình vẫn ra về tay trắng. Mình nhớ như in cái cảm giác đó, cái cảm giác mà mình thực sự đã sinh ra vì một mục đích khác, một mục đích mà không liên quan khỉ gì đến tích phân cả, rằng thì là cả đời này mình cũng vẫn nấp dưới danh nghĩa của một thằng không thể giải được bài tích phân nào trong đề thi ĐH emo_popo_cry

Ấy thế mà lên ĐH, mình lại bá đạo vô cùng về mấy cái thứ gớm ghiếc này, tất cả thay đổi, cuộc sống thay đổi, lòng người cũng thay đổi. Mình từng ghét Vật Lý, rồi mình yêu Vật Lý, mình từng ghét Console, rồi mình yêu Console, mình từng ghét Tích phân, và rồi mình yêu Tích phân. Lý do cũng chỉ có một, đó là nếu ta yêu một ai đó, ta sẽ… không còn ghét họ nữa (đệch after_boom). Thôi nói tóm lại, là một khi mình đã thấy mặt lợi ích của cái gì đó mang lại, thì mình sẽ không còn thấy nó vô bổ nữa…

ops emo_popo_waaaht emo_popo_sweat

Nội dung chính

Ở bài này, chúng ta có một vùng để nhập séc rét, điền thử:

^_^

và nhấn Save, chúng ta có một cái link:

http://challenges.wargame.vn:1337/web300_c4d7c1d9c925b4021adf5e192315ecb9/?file=dbf8adc50a348b9e808d204825c7aed2d4430f900ed7b96daec2a3c1d6665a2792cdb3104d2666e64e069d87cd286765d29b0c16ca1ab2d26249735974d6fb50&sign=730646501b5d8c9a234686a9bcc580bb

Khá dài và khá hãi… Xem source thì có được:

Như đề bài đưa, source nằm ở index.phps, xem nó thôi:

Ta sẽ chia code làm 2 phần chính: Mã hóaGiải mã, còn nhiệm vụ của chúng ta là đọc file ./secret/flag.php.

Mã hóa

$secret_hmac, $secret_filename được tạo ngẫu nhiên, sau đó ghép vào với ký tự ngăn cách là ‘|‘, cuối cùng mã hóa thành tham số file cho url. Tham số còn lại, sign, dùng cho mục đích kiểm tra tính hợp lệ của $secret_filename (mình sẽ trình bày sau).

Nói chung phần này không có gì nhiều, ta kệ nó, cho nó chơi một mình.

Giải mã

Trình tự thực hiện là:

  1. Giải mã tham số file.
  2. Chia làm 2 phần, ngăn cách bởi ký tự ‘|‘.
  3. Phần 1 là $secret_filename, phần 2 là $secret_hmac.
  4. Dùng tham số sign để kiểm tra tính hợp lệ của $secret_filename, nếu ok thì in ra nội dung của nó.

Như thế, chúng ta thấy được 2 việc chính cần làm:

  1. Xác định tham số file để $secret_filename trỏ đến file ./secret/flag.php.
  2. Xác định tham số sign ứng với tham số file để pass bước 4.

Xác định tham số file

Thuật toán mã hóa được sử dụng là AES 128bit (16 byte cho mỗi block), mode CBC, nên chúng ta có thể nghĩ ngay đến Byte Flipping emo_popo_smile

Do $secret_filename là MD5, kích thước 32 byte, chúng ta sẽ can thiệp vào block đầu tiên của cipher, để thay đổi block thứ hai của plain text. Nhưng cũng cần lưu ý rằng block đầu của plain text sẽ thành rác, và chúng ta cần chỉnh sửa đường dẫn của file flag cho phù hợp. Ví dụ, ta có một đường dẫn hợp lệ là:

^#%&$%$@@$#%%^$abcde/../flag.php

Bắt đầu từ url nhận được lúc đầu, ta dùng đoạn code sau để thu được tham số file mong muốn:

Khá ổn emo_popo_nosebleed

Xác định tham số sign

Dù rằng $secret_filename xem chừng đã hợp lệ, ta vẫn chưa đọc được nội dung của nó, do bị bước 4 chặn lại. Để ý rằng, sign được tính như sau:

mặt khác, cả $secret_filename $secret_hmac đều là các thành phần trong tham số file, được ngăn cách bởi ký tự ‘|‘, nhưng task chỉ hiển thị $secret_filename cho ta thấy.

Để có $secret_hmac, điều duy nhất mà ta làm được, đó là Flipping thêm một Byte nữa, mục đích là thay đổi ký tự ‘|‘ thành ký tự khác, khi đó câu lệnh:

sẽ trả về $secret_filename chứa cả $secret_hmac (do không còn ký tự ngăn cách), và bằng cách lấy 16 ký tự cuối, ta có thứ mình cần emo_popo_big_smile

Ta biết rằng ký tự ‘|‘ sẽ ở block 3 ($secret_filename là MD5, kích thước 32 byte = 2 block), nên để thay đổi nó, ta sẽ can thiệp vào block 2 của cipher, mà cụ thể là byte thứ 17. Tất nhiên điều này sẽ làm hỏng $secret_filename, nhưng hiện tại nó không còn quan trọng nữa.

Giờ thì chúng ta đã có thể thoải mái tính sign, like a boss ah adore:

Tổng kết

Flag = mario_CBCm0d3_is_swag_huh_;)

Ghi chú: Chúng ta cần để ý giá trị trả về của mỗi bước Byte Flipping, tránh để xuất hiện ‘|’, do điều này sẽ dẫn đến secret_hmac thu được bị sai.

[WarGame] php? c?

http://wargame.kr:8080/prob/7/

:p

Xem source, được gợi ý về file index.phps, mở nó ra coi:

Mở tiếp file p7.c (http://wargame.kr:8080/prob/7/p7.c):

Chương trình này nhận 1 tham số, chính là cái đầu tiên mà ta nhập vào ở trang php. Nó thực hiện điều gì?

  1. Nếu x < 0: Sai.
  2. x = x + 5.
  3. Nếu x > 4: Sai.
  4. Nếu x < 5: Đúng.

Một số không âm, sau khi cộng thêm 5 mà lại không được lớn hơn 4, thật là kinh dị emo_popo_waaaht

Cơ mà không sao, đề ngắn gọn như vậy, chẳng có gì nhiều để mà nghĩ. Chúng ta đã biết rằng, đối với máy tính mà nói, một số có dấu (signed int chẳng hạn), thì khi đạt đến cực đại của số dương, nó sẽ quay về cực tiểu của số âm. Ví như 2147483647 + 1 thì sẽ không bằng 2147483648, mà bằng -2147483648, đây là điều mà đám IT khô cằn chúng ta biết nhiều hơn so với những con người lãng mạn khác emo_popo_look_down

Như thế, nếu như ở trang php, ta nhập num1 = 2147483647 và num2 =2147483647 + 5 = -2147483644, vấn đề sẽ được giải quyết. Với lưu ý rằng 2 textbox để nhập đã bị giới hạn về 9 ký tự, hẳn là ta sẽ phải mất thêm một chút thời gian mới mong có được flag, zin zin: emo_popo_smile

[OverTheWire] Natas – Level 22

http://natas22.natas.labs.overthewire.org/

only admins can reveal the password…

Nhấn View sourcecode:

Do không hiểu cơ chế của cái hàm header() nên mình rơi vào bế tắc, rất cảm ơn Nam Tóc Xù đã hint mình bài này sweet_kiss

Thực chất vấn đề redirect bằng header() như trên do phía trình duyệt xử lý, chứ không phải là server redirect trực tiếp. Hay nói cách khác, toàn bộ nội dung của file trước khi redirect vẫn được tải về chứ không hề bị ngắt sau khi gọi hàm header(). Ta có thể dễ dàng kiểm tra điều này bằng cách xem tab Network trong Web Developer Tools của trình duyệt.

Để nhận nội dung mà không redirect, ta có thể dùng curl ở chế độ mặc định:

You are an admin. The credentials for the next level are:<br><pre>Username: nata
s23
Password: D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE

→ flag = D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE.

[OverTheWire] Natas – Level 21

http://natas21.natas.labs.overthewire.org/

Note: this website is colocated with http://natas21-experimenter.natas.labs.overthewire.org

You are logged in as a regular user. Login as an admin to retrieve credentials for natas22.

Nhấn View sourcecode:

Thua, đố làm được emo_popo_beat_plaster

Chuyển qua trang kia và chú ý ở:

Xong, còn có thể mong chờ một bài dễ hơn được nữa ở level này không hả trời surrender

Rõ ràng ta có thể edit cái form submit để trang thứ 2 này lưu $_SESSION với các giá trị tùy ý, trong khi trang 1 lại dùng chung session với trang 2. Thế là emo_popo_look_down

  1. Submit trang 2 với một thuộc tính có name:value = admin:1
  2. Đổi biến PHPSESSID của trang 1 thành PHPSESSID của trang 2.
  3. Done.
You are an admin. The credentials for the next level are:

Username: natas22
Password: chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ

→ flag = chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ.

[OverTheWire] Natas – Level 20

http://natas20.natas.labs.overthewire.org/

You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.

Nhấn View sourcecode:

Chúng ta sẽ đi lần lượt theo quy trình xử lý của bài này. Đầu tiên khi ta submit form login:

Quá trình set giá trị cho biến $_SESSION được thực hiện qua hàm mywrite(), đáng chú ý ở:

Tức là giá trị của biến $_SESSION sẽ được lưu vào file thành 1 dòng. Sau đó là quá trình đọc thông tin login của user:

Thao tác lấy giá trị được đảm nhiệm bởi hàm myread():

Có một điểm đặc biệt là, nếu xét đúng bản chất, biến $_SESSION chỉ có thể chứa một thuộc tính có tên là name, tuy nhiên ở hàm myread(), code vẫn đọc từng dòng từ file để gán giá trị, và nó dẫn đến một cánh cửa vô cùng sáng cho ta lao vào, đó là inject ký tự xuống dòng vào username để tạo ra một thuộc tính 2 trong 1 khi đưa vô file still_dreaming

Kết quả:

You are an admin. The credentials for the next level are:<br><pre>Username: nata
s21
Password: IFekPyrQXftziDEsUr3x21sYuahypdgJ

→ flag = IFekPyrQXftziDEsUr3x21sYuahypdgJ.

[OverTheWire] Natas – Level 19

http://natas19.natas.labs.overthewire.org/

This page uses mostly the same code as the previous level, but session IDs are no longer sequential…

Please login with your admin account to retrieve credentials for natas20.

Không có View sourcecode, tuy nhiên dựa vào lời tựa, ta có thể hiểu là nó cũng tương tự bài 18, chỉ khác là giá trị của PHPSESSID sẽ không còn đơn giản nữa.

Thử login với:

username:password = yeuchimse:123456

Kiểm tra cookie:

PHPSESSID=3334332d7965756368696d7365

Khá giống một xâu HEX, decode:

343-yeuchimse

Đơn giản đến không ngờ. Như vậy ta chỉ cần dò giống hệt bài trước, khác mỗi cái cơ chế encode của biến PHPSESSID:

Kết quả là ta tìm được admin tại id = 176

→ flag = eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF.

[OverTheWire] Natas – Level 18

http://natas18.natas.labs.overthewire.org/

Please login with your admin account to retrieve credentials for natas19.

Nhấn View sourcecode:

Bài này sử dụng một custom-session-manager, với giá trị của PHPSESSID nằm ngẫu nhiên trong khoảng từ (1, 640). Tất nhiên admin cũng chỉ có thể nằm trong mớ giá trị đó thôi, và chúng ta sẽ thử dần dần từng giá trị cho đến khi thành công:

Kết quả là SESSID của admin = 84:

→ flag = 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs.

[OverTheWire] Natas – Level 17

http://natas17.natas.labs.overthewire.org/

Nhấn View sourcecode:

Tất cả các thông báo đều đã bị xoẹt xoẹt, làm sao đây emo_popo_cry

Dựa vào cái gì để phân biệt giữa True và False bây giờ emo_popo_pudency

……

………

……

emo_popo_haha

Hãy cùng để ý phần Time-based, vì nó chính là thứ chúng ta cần sweet_kiss

Ý tưởng là, ta sẽ viết một câu query với đặc tính sau:

  • Nếu phép kiểm tra là đúng, sleep 3 giây.
  • Nếu phép kiểm tra là sai, không làm gì cả.

Khi ấy, bằng việc so sánh khoảng thời gian thực hiện việc gửi và nhận request, ta có thể biết được phép thử mình gửi lên là đúng hay sai (tất nhiên không xét trường hợp tốc độ mạng quá chậm, dẫn đến không phân biệt nổi giữa sleep 3 giây và sleep 0 giây emo_popo_look_down)

Code minh họa như sau (sẵn kết hợp cả Binary Search thần thánh, nhưng mình không khuyến khích dùng vì… mình không thích cho dùng sexy_girl):

Kết quả:

xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

→ flag = xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP.