https://foobar.nitdgplug.org/challenges

Crypto:

Pixelite :

we are given with two files: chall.py and pixelite.png

and we are also given this number: 1678519928.9423187

looking at code we know that this code is doing xor of every pixel of flag.png with random int between 0 to 255

flag_matrix[i, j] = tuple(
    map(
        lambda x: x ^ random.randint(0, 255),
        flag_matrix[i, j]
    )
)          

Here the random module of python can be predicted. If we know the seed value than all the next random int are same every time.

random.seed(time.time())

The seed is set to time.time() and we are given this value in challenge: 1678519928.9423187

know we can easily reverse this xor operation to get original image.

import time
import random

from PIL import Image


random.seed(1678519928.9423187)

flag_matrix = (img := Image.open('pixelite.png')).load()
w, h = img.size

for i in range(w):
    for j in range(h):
        flag_matrix[i, j] = tuple(
            map(
                lambda x: x ^ random.randint(0, 255),
                flag_matrix[i, j]
            )
        )

img.save('flag.png')

flag.png:

image

GLUG{Y0u_4Re_noT_5o_w34k}

funwithrandom-1:

description: randcrack is fun or is it . let’s see if you can create your own

nc chall.foobar.nitdgplug.org 30001

file: chall.py

In this code we have rand_gen() function. if mt_index > 624 than it go inside if statement. else it will do the following operations:

y = mt[mt_index]
y ^= (y >> 43)
y ^= (y << 12) & TemperingMaskB
y ^= (y << 67) & TemperingMaskC
y ^= (y >> 69)
mt_index += 1

return y

getstate() Return an object capturing the current internal state of the generator. and the seed is set through os.urandom(8) which is not predictable.

random.seed(os.urandom(8))
mt = list(random.getstate()[1])

here output is filled 624 times with rand_gen() function.

output = []
for _ in range(624):
    output.append(rand_gen())

again looking at this code we know that it will do this operations on mt aaray from 0 to 624 index.

y = mt[mt_index]
y ^= (y >> 43)
y ^= (y << 12) & TemperingMaskB
y ^= (y << 67) & TemperingMaskC
y ^= (y >> 69)
mt_index += 1

return y

and we are given with the output’s value so by reversing this operations we can get the value of mt.

with this small trial and error experiment:

y = 1111121111
y ^= (y >> 43)
print("y ^= (y >> 43)")
print(y)
y ^= (y << 12) & TemperingMaskB
print("y ^= (y << 12) & TemperingMaskB")
print(y)
y ^= (y << 67) & TemperingMaskC
print("y ^= (y << 67) & TemperingMaskC")
print(y)
y ^= (y >> 69)
print("y ^= (y >> 69)")
print(y)
output:
y ^= (y >> 43)
1111121111
y ^= (y << 12) & TemperingMaskB
1736326359
y ^= (y << 67) & TemperingMaskC
1736326359
y ^= (y >> 69)
1736326359

now we know that only y ^= (y << 12) & TemperingMaskB this operation is effective rest are not making any changes.

so now we have to reverse this tempering here is python code for that:

def untemper(y):
    a = y << 12
    b = y ^ (a & TemperingMaskB)
    c = b << 12
    d = y ^ (c & TemperingMaskB)
    e = d << 12
    f = y ^ (e & TemperingMaskB)
    g = f << 12
    h = y ^ (g & TemperingMaskB)
    i = h << 12
    final = y ^ (i & TemperingMaskB)
    return final

now from output[] we can get mt[]. but the for loop was run 624 times so next time we call rand_gen() it will go inside if condition.


for kk in range(N - M):
    y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK)
    mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1]

for kk in range(N - M, N - 1):
    y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK)
    mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1]

y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK)
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1]

we will apply the same changes to our recovered mt[].

now we can get the next 5 int by applying this operations to first 5 elements of mt[].

y = mt[mt_index]
y ^= (y >> 43)
y ^= (y << 12) & TemperingMaskB
y ^= (y << 67) & TemperingMaskC
y ^= (y >> 69)
mt_index += 1

now that we have the next 5 random element we can get the flag.

final python script:

import random
from pwnlib.tubes.remote import remote

N = 624
M = 397
MATRIX_A = 0x83a2b0c3
UPPER_MASK = 0x80000000
LOWER_MASK = 0x7fffffff
TemperingMaskB = 0x3f5663d0
TemperingMaskC = 0x56e90000
mag01 = [0, MATRIX_A]


def untemper(y):
    a = y << 12
    b = y ^ (a & TemperingMaskB)
    c = b << 12
    d = y ^ (c & TemperingMaskB)
    e = d << 12
    f = y ^ (e & TemperingMaskB)
    g = f << 12
    h = y ^ (g & TemperingMaskB)
    i = h << 12
    final = y ^ (i & TemperingMaskB)
    return final

conn = remote('chall.foobar.nitdgplug.org', 30001)
conn.recvuntil("Generator\n\n".encode())
output = [int(i) for i in (conn.recvline().decode()[1:-2].split(','))]
print(output)
for i in range(len(output)):
    output[i] = untemper(output[i])
# print(output)
mt = output
for kk in range(N - M):
    y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK)
    mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1]

for kk in range(N - M, N - 1):
    y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK)
    mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1]

y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK)
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1]

# print(mt)
mt_index = 0
for i in range(5):
    y = mt[mt_index]
    y ^= (y >> 43)
    y ^= (y << 12) & TemperingMaskB
    y ^= (y << 67) & TemperingMaskC
    y ^= (y >> 69)
    mt_index += 1
    conn.recvuntil(":".encode())
    print(f'{i+1}= {y}')
    conn.sendline(f'{y}'.encode())

print(conn.recvline().decode())
conn.close()

flag: GLUG{R4nd0m_Numb3r_G3n3r470r_15_tru3ly_r4nd0m_0r_15_17}

Web:

inspect:

Description: Don’t think too much. Just push to production http://chall.foobar.nitdgplug.org:30045/

Rest API was boring so I used modern technology.

Let’s open this website

image

Hmn Cannot GET /

I tried robots.txt and checked http response headers but nothing, so I did directory bruteforce and got this endpoint: /graphql GraphQL is a query language developed by Facebook

http://chall.foobar.nitdgplug.org:30045/graphql

image

Reference : https://blog.yeswehack.com/yeswerhackers/how-exploit-graphql-endpoint-bug-bounty/

Introspection is the ability to query which resources are available in the current API schema. Given the API, via introspection, we can see the queries, types, fields, and directives it supports.

GraphQL introspection payload:

{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}

response:

image

This secret field looks interesting let’s extract this.

payload:

{
  secret {
    text
  }
}

response:

image

when I saw the flag I immediately tried to submit it, but it was wrong then I realised that there are multiple flags.

75 in total.

first I thought it is rabbit hole, but I went through every flag and found this:

image

this makes sense inspect is challenge name and graphql is endpoint.

flag: GLUG{1nsp3c7_1n_gr4phq6}

This is correct one.

Happy Hacking