---
# System prepended metadata

title: INTIGRITY_1337UP_2023

---



## INTIGRITY_1337UP_2023
---
![image](https://hackmd.io/_uploads/B1nDKVv4a.png)

Đợt vừa rồi (17 ~ 18/12/2023) team mình có tham gia giải 1337UP mình thấy các crypto challs khá hay và không quá khó nên mình chia sẻ một vài write-ups. 


---
### Really Secure Apparently

Describtion:
```l!
Apparently this encryption is "really secure" and I don't need to worry about sharing the ciphertext, or even these values..

n = 689061037339483636851744871564868379980061151991904073814057216873412583484720768694905841053416938972235588548525570270575285633894975913717130070544407480547826227398039831409929129742007101671851757453656032161443946817685708282221883187089692065998793742064551244403369599965441075497085384181772038720949
e = 98161001623245946455371459972270637048947096740867123960987426843075734419854169415217693040603943985614577854750928453684840929755254248201161248375350238628917413291201125030514500977409961838501076015838508082749034318410808298025858181711613372870289482890074072555265382600388541381732534018133370862587
```
[Ciphertext](https://ctf.intigriti.io/files/e7e478273a8fa6853d87f71408401b9b/ciphertext?token=eyJ1c2VyX2lkIjoxMDA1LCJ0ZWFtX2lkIjo0MjksImZpbGVfaWQiOjIwfQ.ZVm7Gw.1vUuRfvJ-L4EbdIjZJiidqKCDt8)

*  Bài này dễ thấy là một RSA encryption cơ bản nhưng ta không thể phân tích $n=p*q$ vì $n$ là một safe prime. Sau một hồi suy nghĩ mình nhận thấy $e$ lớn ($\approx$ $n$) không như các bài khác $=>$ $d$ nhỏ.

* Vì vậy mình dùng ~~tool~~ wiener attack trong [link này](https://github.com/orisano/owiener) 

Solve.py:
```python=
from Crypto.Util.number import *
from math import gcd
import owiener
f = open('ciphertexe', 'rb')

file = f.readline()
ct = file
n = 689061037339483636851744871564868379980061151991904073814057216873412583484720768694905841053416938972235588548525570270575285633894975913717130070544407480547826227398039831409929129742007101671851757453656032161443946817685708282221883187089692065998793742064551244403369599965441075497085384181772038720949 
e = 98161001623245946455371459972270637048947096740867123960987426843075734419854169415217693040603943985614577854750928453684840929755254248201161248375350238628917413291201125030514500977409961838501076015838508082749034318410808298025858181711613372870289482890074072555265382600388541381732534018133370862587

d = owiener.attack(e, n)
#print(d)

pt = long_to_bytes(pow(bytes_to_long(ct), d, n))

print(pt)
```

![Screenshot 2023-11-19 144927](https://hackmd.io/_uploads/B1R7AVDV6.png)

**Flag : INTIGRITI{0r_n07_50_53cur3_m4yb3}**

## Keyless

> Describtion:
> My friend made a new encryption algorithm. Apparently it's so advanced, you don't even need a key!
> 
Chall.py:
```python=
def encrypt(message):
    encrypted_message = ""
    for char in message:
        a = (ord(char) * 2) + 10
        b = (a ^ 42) + 5
        c = (b * 3) - 7
        encrypted_char = c ^ 23
        encrypted_message += chr(encrypted_char)
    return encrypted_message

flag = "INTIGRITI{REDACTED}"
encrypted_flag = encrypt(flag)

with open("flag.txt.enc", "w") as file:
    file.write(encrypted_flag)
```
> Flag.txt:
> ``ȽƻǇȽȉƃȽǇȽΑɥćʝʣāʹćʹɱāʝʹɩßʵɷ˧ʹʣāʹʣāééāɃʹć˫éāɃʹćɷɷ΅``

* Bài này thì mình chỉ việc làm ngược lại những gì đã encrypt =)) và kiểm tra xem kí tự ascii có nằm trong khoảng thoả mãn không. 

Solve.py:
```python=
f = open("flag.txt.enc", 'r')
import string
flag = ""
ct = f.readline()
for i in ct:
    c = ord(i)^23
    #print(c)
    b = (c+7)//3
    #print(b)
    a = (b-5)^42
    #print(a)
    d = (a-10)//2
    #print(chr(d))
    if chr(d) in string.printable:
        flag += chr(d)
print(flag)
```
**Flag : INTIGRITI{m4yb3_4_k3y_w0uld_b3_b3773r_4f73r_4ll}**

## Not So Smooth:
> Description: Can you find a and b?
> 
Chall.py:
```python=
from Crypto.Util.number import long_to_bytes
from Crypto.Util.strxor import strxor
from random import randint
from flag import FLAG

def f(x, n):  
    return (pow(u,n,p)*x + v*(1-pow(u,n,p))*pow(1-u, -1, p)) % p  

p = 97201997431130462639713476119411091922677381239967611061717766639853376871260165905989218335681560177626304205941143288128749532327607316527719299945637260643711897738116821179208534292854942631428531228316344113303402450588666012800739695018334321748049518585617428717505851025279186520225325765864212731597
u = 14011530787746260724685809284106528245188320623672333581950055679051366424425259006994945665868546765648275822501035229606171697373122374288934559593175958252416643298136731105775907857798815936190074350794406666922357841091849449562922724459876362600203284195621546769313749721476449207319566681142955460891977927184371401451946649848065952527323468939007868874410618846898618148752279316070498097254384228565132693552949206926391461108714034141321700284318834819732949544823937032615318011463993204345644038210938407875147446570896826729265366024224612406740371824999201173579640264979086368843819069035017648357042
v = 16560637729264127314502582188855146263038095275553321912067588804088156431664370603746929023264744622682435376065011098909463163865218610904571775751705336266271206718700427773757241393847274601309127403955317959981271158685681135990095066557078560050980575698278958401980987514566688310172721963092100285717921465575782434632190913355536291988686994429739581469633462010143996998589435537178075521590880467628369030177392034117774853431604525531066071844562073814187461299329339694285509725214674761990940902460186665127466202741989052293452290042871514149972640901432877318075354158973805495004367245286709191395753
w = 30714296289538837760400431621661767909419746909959905820574067592409316977551664652203146506867115455464665524418603262821119202980897986798059489126166547078057148348119365709992892615014626003313040730934533283339617856938614948620116906770806796378275546490794161777851252745862081462799572448648587153412425374338967601487603800379070501278705056791472269999767679535887678042527423534392867454254712641029797659150392148648565421400107500607994226410206105774620083214215531253544274444448346065590895353139670885420838370607181375842930315910289979440845957719622069769102831263579510660283634808483329218819353
a = randint(0, 2**2048)
b = randint(0, 2**2048)
A = f(w, a)
B = f(w, b)
key = long_to_bytes(f(B, a))[:len(FLAG)]
enc = strxor(FLAG, key)
print(f"{A = }")
print(f"{B = }")
print(f"{enc = }")

"""
A = 7393401480034113709683683682039780458211722756040975666277858366986963864147091724359492764726999692812421940595309756560491142512219957986281425163574890752574157617546760386852366936945888357800966704941013951530688031419816817272581287237223765833452303447283089906937413964658335387593899889933721262202
B = 6919381992041136573008188094979879971060160509085428532054694712745921654244468113796582501225839242977870949915769181804595896718922228206397860738237256125972615830799470450058633231003927061049289907097099916321068776956652172887225970642896455423957706532253349472544176183473470843719479781727784095989
enc = b'\xcfW\x85\x8d\xedU\xdd\xd9`\x16f\xb8j(\xeb9-\x1b\xb8\x18 0av\xe5\xabK\xc6' 
"""

```

$$
  f(x, n) = u^n*x + v*{\frac{1-u^n}{1-u}} \mod p
$$
$$
A = f(w, a) = u^a*w + v*{\frac{1-u^a}{1-u}} \mod p
$$
$$
B = f(w, b) = u^b*w + v*{\frac{1-u^b}{1-u}} \mod p
$$
$$
key = f(B, a) = u^a*B + v*{\frac{1-u^a}{1-u}} \mod p
$$
* Vì $A$ và $key$ có chung $u^{A}$ nên khi ta có thể biểu diễn $key$ theo $A$ cụ thể sau:

$$
u^{a} = {\frac{A*(1-u)-v}{w*(1-u)-v}} = {\frac{key*(1-u)-v}{B*(1-u)-v}}
$$
* Đến đây mình biểu diễn $key$ theo các tham số đã cho và tính toán $key$ là có flag.

Solve.py:
```py=
from sage.all import*
from Crypto.Util.number import* 
from Crypto.Util.strxor import strxor
A = 7393401480034113709683683682039780458211722756040975666277858366986963864147091724359492764726999692812421940595309756560491142512219957986281425163574890752574157617546760386852366936945888357800966704941013951530688031419816817272581287237223765833452303447283089906937413964658335387593899889933721262202
B = 6919381992041136573008188094979879971060160509085428532054694712745921654244468113796582501225839242977870949915769181804595896718922228206397860738237256125972615830799470450058633231003927061049289907097099916321068776956652172887225970642896455423957706532253349472544176183473470843719479781727784095989

enc = b'\xcfW\x85\x8d\xedU\xdd\xd9`\x16f\xb8j(\xeb9-\x1b\xb8\x18 0av\xe5\xabK\xc6'

def f(x, n):  
    return (pow(u,n,p)*x + v*(1-pow(u,n,p))*pow(1-u, -1, p)) % p  

p = 97201997431130462639713476119411091922677381239967611061717766639853376871260165905989218335681560177626304205941143288128749532327607316527719299945637260643711897738116821179208534292854942631428531228316344113303402450588666012800739695018334321748049518585617428717505851025279186520225325765864212731597
u = 14011530787746260724685809284106528245188320623672333581950055679051366424425259006994945665868546765648275822501035229606171697373122374288934559593175958252416643298136731105775907857798815936190074350794406666922357841091849449562922724459876362600203284195621546769313749721476449207319566681142955460891977927184371401451946649848065952527323468939007868874410618846898618148752279316070498097254384228565132693552949206926391461108714034141321700284318834819732949544823937032615318011463993204345644038210938407875147446570896826729265366024224612406740371824999201173579640264979086368843819069035017648357042
v = 16560637729264127314502582188855146263038095275553321912067588804088156431664370603746929023264744622682435376065011098909463163865218610904571775751705336266271206718700427773757241393847274601309127403955317959981271158685681135990095066557078560050980575698278958401980987514566688310172721963092100285717921465575782434632190913355536291988686994429739581469633462010143996998589435537178075521590880467628369030177392034117774853431604525531066071844562073814187461299329339694285509725214674761990940902460186665127466202741989052293452290042871514149972640901432877318075354158973805495004367245286709191395753
w = 30714296289538837760400431621661767909419746909959905820574067592409316977551664652203146506867115455464665524418603262821119202980897986798059489126166547078057148348119365709992892615014626003313040730934533283339617856938614948620116906770806796378275546490794161777851252745862081462799572448648587153412425374338967601487603800379070501278705056791472269999767679535887678042527423534392867454254712641029797659150392148648565421400107500607994226410206105774620083214215531253544274444448346065590895353139670885420838370607181375842930315910289979440845957719622069769102831263579510660283634808483329218819353

key = (((A*(1-u)-v)*(B*(1-u)-v)*inverse(w*(1-u)-v, p) % p) + v )*inverse(1-u, p) % p
#print(long_to_bytes(key))
flag = strxor(enc, long_to_bytes(key)[:len(enc)])
print(flag)
```
**Flag : INTIGRITI{1e863724be1ea6d3e}**

## Share-it(part 1)



> Description:
> Check out my new cool platform for sharing your thoughts. I used my 1337 crypto skills to craft session tokens 🔐
> 
app.py:
```python=
from flask import Flask, render_template, request, redirect, url_for, make_response
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
import json
import base64
from waitress import serve

app = Flask(__name__)

key = os.urandom(16)

FLAG = os.getenv("FLAG")
if not FLAG:
    FLAG = "FLAG{dummy}"


def gen_encrypted_cookie(username):
    iv = os.urandom(16)
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    user_dict = {'admin': False, 'username': username}
    c = cipher.encrypt(pad(json.dumps(user_dict).encode(), 16))
    return base64.b64encode(json.dumps({'user_dict': base64.b64encode(c).decode(),
                                        'iv': base64.b64encode(iv).decode()}).encode()).decode()


def decrypt_cookie(cookie):
    cookie_dict = json.loads(base64.b64decode(cookie).decode())
    iv = base64.b64decode(cookie_dict['iv'].encode())
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    return json.loads(unpad(cipher.decrypt(base64.b64decode(cookie_dict['user_dict'].encode())), 16))


@app.route("/")
def index():
    cookie = request.cookies.get("token")
    if cookie == None:
        return redirect(url_for('register'))
    else:
        try:
            user_dict = decrypt_cookie(cookie)
        except:
            return redirect(url_for('register'))
        return render_template('index.html', username=user_dict['username'])


@app.route("/register", methods=['GET', 'POST'])
def register():
    if request.method == "GET":
        return render_template("register.html")
    elif request.method == "POST":
        username = request.form["username"]
        if username == None or username == "":
            return "username must be set", 400
        cookie = gen_encrypted_cookie(username)
        res = make_response(redirect(url_for('index')))
        res.set_cookie('token', cookie)
        return res


@app.route("/admin-opinions")
def admin():
    cookie = request.cookies.get("token")
    if cookie == None:
        return redirect(url_for('register'))
    else:
        try:
            user_dict = decrypt_cookie(cookie)
        except:
            return redirect(url_for('register'))
        if not user_dict['admin'] == True:
            return "<p>Only admins are allowed to read these cool opionons</p>", 403
        else:
            return render_template("admin.html", flag=FLAG)


if __name__ == '__main__':
    print("Starting app...")
    serve(app, host='0.0.0.0', port='5000')

```

* Cách hoạt động của page này là đăng kí username, gắn **"admin = False"** và encode CBC và trả về cookies của page các giá trị ciphertext & iv
 
* Ta cần phải truyền tham số trên vào cookies của /admin-opinions sao cho khi decrypt thì nhận được "admin = True" khi đó ta mới có flag.

* Tuy nhiên, mình có thể chỉnh sửa iv và có thể gửi chung với ciphertext đồng thời có thể thấy rằng "admin = False" nằm ở block đầu của ciphertext $=>$ Đây chính là lổ hổng để ta khai thác CBC bitflipping attack.

![image](https://hackmd.io/_uploads/Sk6UBrP4a.png)

* Vì vậy ta chỉ cần sửa sao cho iv_new[-7:-1] khi xor với pt' sẽ thành b'True' 

Solve.py:
```python=
import requests
import json
from Crypto.Util.number import*
from base64 import b64decode, b64encode
import base64
from Crypto.Util.Padding import pad, unpad
import json
from Crypto.Cipher import AES
from Crypto.Util.number import*
import os
key = os.urandom(16)
def xor(a:bytes, b:bytes)->bytes:
    return bytes([i^j for i,j in zip(a,b)])
session = requests.Session()
username = "adnDasdadsa"
user_dict = {'admin': False, 'username': username}

response = session.post("https://shareit.ctf.intigriti.io/register", data={"username": username})

token =json.loads(str((session.cookies.get_dict())).replace("'", '"'))['token']
token = json.loads(b64decode(token).decode())
user_dict = b64decode(token['user_dict'])
iv = b64decode(token['iv'])

iv_new = iv[:-7] + xor(xor(iv[-7:-1], b' true '), b' false') + iv[-1:]

token = b64encode(json.dumps({'user_dict': b64encode(user_dict).decode(), 'iv': b64encode(iv_new).decode()}).encode()).decode()
print(token) 
res = requests.get("https://shareit.ctf.intigriti.io/admin-opinions", cookies={"token":token}).text
print(res)
```
**Flag : INTIGRITI{1v_1ike_t0_fl1p_bit5}**

## 1-10
> 
> Description: One equation, ten unknowns?
> Note: flag format is INTIGRITI{[0-9a-f]}
> 

Chall.py:
```python=
from random import randint
from re import search
from flag import FLAG

cs = [randint(0, 2**1000) for _ in range(10)]
xs = [randint(0, 2**64) for _ in range(10)]
xs = [ord(f) + i - (i%1000)  for i, f in zip(xs, search("{(.*)}", FLAG).group(1))]

print(f"{cs = }")
print(f"s = {sum(c*x for c, x in zip(cs, xs))}")

```

* Các giá trị của xs ~ 64 bits $\ll$ 1000 bits ~ cs nên bài này mình dùng LLL latice reduction để tìm lại các hệ số xs.

* Ngoài ra mình chú ý rằng $i - i\mod1000 = 1000k$ hay 3 chữ số cuối luôn bằng = 0 $=>$ 3 chữ số cuối của mỗi số chính là ord(f) 

Solve.py:
```python=
from sage.all import *
import numpy as np
cs = [8508903290440008966939565321248693758153261635170177499193552423579929500027826696702216711413627480472568726828904707392607240309148374882044455682656477650413559779578913981575195542381602155806438946382809049847521263107908111429547314575039079118614485792613461747911710760754291582134293099750060, 10234293217173095983648586990138462404689872504690765936890158736280331352728086141006820545673419953576281340699793983414878095413526583845311613647542879798224462254801103246845064675391113534349390649562211376117941776588135441368773636568930887968431002105334751994385414474789708434897717472259757, 6001064586644974650131784742218587067958465984737568290249286706923485137083921908971767187010824715217158349948368322929900720010489749231105336650564421771867089333709608235963711368415685056362117910529113580811922176651335662802405504434103542105450330213217418470901029864459362153866361049469621, 5859510800336462649673113647904370677448984650623412649303149431740483580968255760095323745895405406649271411277663981671465673293279417168147656423009231087547991428322779036740050269460373254323377738756038706795196225547099530503996157675637620918729310987613041873955654973230573780794437230183289, 8212120161226957435594246142362544687871307206030517377713172267061914524817671684448986080347503212333314134144272096534190656954277299391948626024244379808998220515649968150824587976113971840005858079163744362874678111323034234960076591622752217194796532407435861854992608669653483268713825154541681, 4292538496747452556903766205458518557016170261915268175117554973221631407580344459540989898488936014316805799620957521118332103032738032797936315597220903773140347787977387271254963436603728977128756213671653297994336981775219965231686927050793105808729293803455246360077380768093287937551667515822737, 8583458084429417950887051233123781099671792568724013361916924355046040863544385972858215904752358387759143712618915109914726815547284050405347634520790328222420443989299783668017365846692013464579110450651166600940834254189911732107856656458621485902792541383514622551498513045029193930072821693821256, 927938350277846540058170699346614173130036388369329189433895716040551556863284640834396837739290832786836335265440745786025530973467859153202044442045287145528583412999497854136387626360287750242048999254798532603013016406637079389023297629455299864761196574249382738851682248453939600976884575974199, 4606866838328488359534883828872534448488908284003992208192170511899852596906485417934690617926601159129473558885893097400239110669875450476234618534668886892219546199419412794765402627731086862572263105282498567494065303352715044800789544479262215220148659740517187562922289802434925672447697743660640, 5696622808956926263797513675882969816326582766528835713485415099018508834817057303528828064039948371652175876967703746446602159940653502950606513683435185458750394450192106019388424601807240033502531431423705043713657847236861816929000927218441444067742560786753091009546483807078198791541719979069795]
s = 605466527953516222016485516214431809590993588699320208021845670703468281059947406248463347211427615855012720451029976981068579151311047123161756448068506197424807516350675172131826275005312472029312861168498961728971558322943730466676859739724928104907194812943584226111451426124864722285484117269190235012612078303171378
#print(len(cs))
a = [[0 for i in range(11)] for i in range(11)]
for i in range(1, 11):
    for j in range(1, 11):
        if i==j:
            a[i][j]=1

a[0] = [s] +  cs

a = Matrix(a)
a = a.T
L = a.LLL()
ls = np.matrix(L[0]).tolist()[0][1:]
flag = ''
for i in ls:
    flag = flag + (chr(int(str(i)[-3:])))
    
flag = "INTIGRITI{" + flag + '}'
print(flag)
```

**Flag = INTIGRITI{3a8a32c7f6}**

---
