SQLi ở key của biến mảng

hkln1
tradahacking
Published in
4 min readSep 19, 2019
  • Lại nói về SQLi ^ ^, cái lỗi này nó có từ thời khủng long… Những năm gần đây các modern website thường được viết bằng các framework có các hàm build-in chống SQLi sẵn. Nên việc tìm lỗi này cũng rất hiếm. Tuy nhiên hiếm không có nghĩ là không có. Nếu ta có phương pháp thích hợp cộng một chút may mắn thì hoàn toàn có duyên gặp nó ;). Thông thường khi chơi nhưng public program có scope rộng kiểu *.abc.com và khi test những site sub-domain tui thường test rất kỹ SQLi. Nhưng đối với main-site, tui chỉ tập trung vào test một số điểm có khả nghi bị SQLi. Nếu không tìm được thì next sang các lỗi khác (Bởi vì tui lười… mà cái tính này không tốt à nghen…các bạng cứ kiên trì test hết đi ^ ^). Các điểm mà tui thường test là: Cookie, User-agent, api chức năng search, graphql, input vào mệnh đề order by/group by, column name, key của biến mảng kiểu paramname[inject_here]=value, khu vực signup, login, forgot password hay chọn lọc 1, 2 chức năng insert/update/delete đại diện để test. Ngoài ra tuỳ ứng dụng tui thấy khả nghi chỗ nào thì ngoại cảm test đại thôi :v.
  • Gần đây tui có gặp một case khá là vui. Sau khi submit vài lỗi SQLi, tui cũng chịu khó bào hết các endpoint này nọ mà vẫn không tìm được gì thêm. Nhưng với kinh nghiệm của mình, tui linh cảm nó vẫn còn đâu đó…Chắc đã đến lúc thinking out of the box rùi :3. Ah, cũng nói thêm, cái app này dzui dzui là nó chặn ko cho view source trên browser nhưng trong response thì nó show ra câu SQL query khi người dùng thực thi các function mà nó phải query xuống DB =)). Chính nhờ nó mà tui mới tìm ra được lỗi này (thank bạng dev dễ thương).
  • Sau khi làm đủ kiểu, tui chọn ra endpoint này để chơi với nó. Lý do tui chọn nó là vì tất cả những endpoint khác một là không bị lỗi (cái này có thể khẳng định chắc chắn vì tui thấy kết quả nó filter ở response trả về như đã nói ở trên), hai là có lỗi do tui tìm ra. Nếu endpoint này thật sự có lỗi thì tui sẽ không bị dup với chính mình :D.
  • Tui GET request https://hihi.com/search?bien1=ABC. Response trả về đính kèm câu SQL (dài lắm tui đã lược bỏ bớt):

SELECT linhtalinhtinhmotdong FROM bangnaybangno xx JOIN bangnaybangno yy ON gido= gido WHERE linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND code IN ('ABC') AND linhtalinhtinh GROUP BY linhtalinhtinh

  • Trong câu truy vấn chỗ có ' thật ra là single quote, tại nó đính kèm với html trả về cho browser nên có dạng đó, thật ra ở DB nó sẽ xử lý như là single quote. Giá trị ABC của bien1 được filter tốt, vì khi nhập ABC’ -> IN (‘ABC\’’). Thêm ABC\’ -> IN (‘ABC\\\’’).
  • Lúc này tui thấy có 1 endpoint https://hihi.com/search?bien2=all&bienla=16. Tui mới thử request https://hihi.com/search?bien1=ABC&bienla=16 (thêm bienla=16 vào endpoint đang test) thì không thấy có giá trị của biến nào input vào câu query.
  • Tiếp tục tui thử https://hihi.com/search?bien1=ABC&bienla[]=16 → lại không có gì xảy ra.
  • Bực mình tui thử tiếp https://hihi.com/search?bien1=ABC&bienla[][]=16 → vẫn không có gì :(
  • Thế là tui thử thêm https://hihi.com/search?bien1=ABC&bienla[xxxx][]=16
  • Oh,sh*t…nhìn xem nó trả gì về kìa :3

SELECT linhtalinhtinh FROM bangnaybangno WHERE columnnamethisthat IN ('ABC') AND linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND linhtalinhtinh AND linhtatlinhtinh AND ( ( ( MATCH(columnnamethisthat) AGAINST('"xxxx_16"' IN BOOLEAN MODE) ) ) ) ORDER BY tinhtalinhtinh LIMIT 25;

  • bienla[xxxx][]=16 thành xxxx_16 (trong mệnh đề MATCH-AGAINST).
  • Tại đây có thể các bạng sẽ thắc mắc tại sao tui lại có suy nghĩ test kiểu đó. Các bạn có thể tham khảo nàynày (tui lấy cảm hứng từ đó đó, hiuhiu :v). Tiếp tục để break ngữ cảnh câu query tui thử thêm single quote vào giá trị 16 thì thấy nó xoá dấu này. Nhưng khi thêm dấu single quote tại xxxx thì nó thành ( ( ( MATCH(columnnamethisthat) AGAINST('"xxxx’_16"' IN BOOLEAN MODE) ) ).
  • WoW, nó không filter chỗ key trong biến mảng. Thế là tui quốc liền cái payload để thử ' and false or benchmark(100000000,rand()) or ‘0’=’, nhưng nó chạy không thành công, tui liền ‘+sleep(5)+’ rồi ‘+(select sleep(5))+’ cũng không thành công luôn. Kỳ lạ, bởi vì đây là những payload áp dụng cho các ngữ cảnh String mà không quan tâm vị trí nó input ở đâu trong câu query. Bạn có thể xem thử cái này (ah, đối với việc detect SQLi, tui khuyên các bạn nên dùng các payload Time-based. Vì đỡ mắc công các bạn phải tìm điểm khác nhau giữa hai trường hợp true-false (Boolean-based) và các ứng dụng giờ cũng hiếm web nào phun lỗi (Error-based)). Thế là cuối cùng tui dùng payload ‘+(select*from(select(sleep(20)))a)+’ thì nó lại chạy. Thế là tui viết script để PoC https://pastebin.com/c19DbgaN.
  • Bởi vì case của tui có sự hỗ trợ của yếu tố đính kèm câu query trong response nên tui có thể manual tốt. Tuy nhiên nếu trường hợp blackbox, sử dụng bộ payload fuzzer mà tui đã có đề cập trong write up này để test vẫn ok nhé. Happy hacking !

--

--