Re: [Writeup] Chủ đề Crypto - Vòng Chung kết WhiteHat Grand Prix 2014
Crypto 400
1. Phân tích kỹ thuật:
Có thể thấy, với mỗi chuỗi nhập vào sẽ trả về 1 giá trị token
Xem qua source code, có 1 comment
Truy cập đến đường dẫn này để láy source code backup:
Giải nén thu được 3 file: index.php, controller.php, encryption.php
Xem qua source code, có thể xác định được dòng dữ liệu đi như sau
Download file encryption.js:
Ngoài ra có một số nhận định:
- Index.php mặc định gán cookie cho người dùng là Iam=encrypt(“man”), nếu cookie là Iam=encrypt(“superman”) thì in ra flag, trong đó encrypt() là hàm mã hóa.
- encryption.php không thể truy cập trực tiếp do bị chặn IP khác IP local, và nếu msg truyền vào có chứa từ “man” thì sẽ bị chuyển thành “zzz” trước khi mã hóa, nên việc thử mã hóa “superman” trực tiếp vào đây là bất khả thi.
encryption.js là core của việc sinh token, xem code cẩn thận chúng ta biết đây là cài đặt mã hóa AES bằng javascript. Tuy nhiên key mã hóa thì đã được ẩn đi
Google đoạn code để lấy thêm thông tin, các bạn sẽ thấy là đoạn cài đặt AES này có lỗi bảo mật với hàm SubBytes
Đoạn mã hóa aes như sau
Mã:
function aes_encrypt(msg) {
var w = new Array(44);
var state = new Array(16);
var round;
msg = get_value("Message", msg, true);
w = key_expand(key);
state = transpose(msg);
state = AddRoundKey(state, w, 0);
for (round = 1; round < 10; round++) {
state = SubBytes(state, S_enc);
state = ShiftRows(state);
state = MixColumns(state);
state = AddRoundKey(state, w, round * 4 * 4);
}
SubBytes(state, S_enc);
ShiftRows(state);
AddRoundKey(state, w, 10 * 4 * 4);
AES_output = transpose(state);
return format_AES_output("hex");
}
state là mảng có 16 phần từ và là kết quả của các hàm lấy dữ liệu đầu(transpose) vào và xáo trộn với khóa con(AddRoundKey). State sau đấy được truyền vào hàm subbytes()
Mã:
function SubBytes(state, Sbox) {
var i;
for (i = 0; i < 16; i++)
state[i] = Sbox[state[i]];
return state;
}
Chú ý là sbox là mảng có 256 phần tử, vậy nếu state
lớn hơn 256 thì sẽ có lỗi, điều này xảy ra khi msg truyền vào chứa ký tự unicode(mã > 256)
Dựa vào lỗi này, chúng ta có thể thu được key mã hóa bằng cách sử dụng hàm trên:
• Mã hóa một chuỗi unicode 16 byte, chẳng hạn như ưưưưưưưưưưưưưưưư
• Giải mã
• XOR mỗi ký tự của kết quả giải mã với 0x52 để thu được khóa
2. Tiến hành khai thác
2.1. Mã hóa chuỗi unicode
Với mỗi giá trị username nhập vào, msg gửi đi là kết quả của hàm md5 đã được substring 16 byte ở controller.php
Mã:
$msg = substr(md5($return['username']), 0, 16);
Đi qua hàm này thì unicode sẽ bị chuyển về ký tự đọc được nên việc gửi unicode trực tiếp qua textbox sẽ không có tác dụng.
May mắn là ngoài việc gửi username thì index.php còn gửi kèm giá trị timestamp, và biến này thì không bị lọc đầu vào. Chúng ta có thể lợi dụng lỗi HTTP Parameter Pollution ở controller.php để fake giá trị của msg mà ecryption.php nhận được.
Request POST gửi đi
Mã:
POST /crypto/controller.php HTTP/1.1
username=ping&action=encrypt×tamp=141379820170%26msg%3d%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0%C6%B0
Thu được token: c091d348acd3467bafc04f004169bb23
Đây chính là giá trị mã hóa của chuỗi ưưưưưưưưưưưưưưưư
2.2 Giải mã kết quả
Ở controller.php, thấy ngay action có thể nhận một trong 2 giá trị encrypt/decrypt tương ứng với hàm sinh mã và giải mã.
Giải mã bằng Request
Mã:
POST /crypto/controller.php HTTP/1.1
username=c091d348acd3467bafc04f004169bb23&action=decrypt×tamp=141379820170
Thu được token = c22e45bc7f37dad688226edf2b1f40e2
Đoạn code sau thực hiện việc xor từng byte của kết quả giải mã với 0x52 để thu được key
Mã:
__author__ = 'Ping'
cipher = "c2 2e 45 bc 7f 37 da d6 88 22 6e df 2b 1f 40 e2".split(' ')
key = []
for c in cipher:
key.append(hex(int(c, 16) ^ int('52', 16))[2:])
key = ''.join(key)
print key
#907c17ee2d658884da703c8d794d12b0
2.4. Mã hóa để lấy cookie đúng
Để có thể đọc được flag thì cần mã hóa chuỗi “superman” và lưu vào cookie Iam
Sử dụng khóa tìm được ở trên thay vào file encryption.js
Sau đó thực thi, ở đây tôi sử dụng nodejs
Mã:
node encryption.js enc c3VwZXJtYW4=
chuỗi base64 trên là kết quả của encode "superman"
Kết quả thu được: 63b8c3be22502c32b05269b2cb7e6075
Thay cookie Iam bằng giá trị trên để lấy Flag