Uwusignatures CTF Challenge Solution
Challenge Overview
Challenge Name: Uwusignatures Category: Crypto Points: 261 Author: elijah5399
Description:
As an uwu girl, I decided to make this digital signature scheme to share my signatures with everyone!
I'll only show you half of my signature though, because I'm shy...
Surely, no one would steal from a cutie like myself... right?
Connection: nc challs.nusgreyhats.org 33301
Initial Analysis
The challenge provides a Python file uwusignatures.py implementing a custom digital signature scheme. Let's examine the key components:
Code Analysis
class Uwu:
def __init__(self, keylen):
self.p = getPrime(keylen)
self.g = getRandomRange(1, self.p)
self.x = getRandomRange(2, self.p) # private key
self.y = pow(self.g, self.x, self.p) # public key
self.k = getRandomRange(1, self.p)
while GCD(self.k, self.p - 1) != 1:
self.k = getRandomRange(1, self.p)
print(f"{self.p :} {self.g :} {self.y :}")
print(f"k: {self.k}") # 🚨 CRITICAL VULNERABILITY!Key Observations:
This is a variant of ElGamal digital signatures
CRITICAL FLAW: The server prints the nonce
kvalue!The same
kis reused for all signatures (not regenerated per signature)To get the flag, we need to forge a signature for
"gib flag pls uwu"
Signature Scheme Details
Signing: s = ((h - x * r) * k^(-1)) mod (p-1)
Verification: g^h ≡ y^r * r^s (mod p)
Where:
h = SHA256(message)r = g^k mod px= private keyk= nonce (leaked!)
The Vulnerability: K-Reuse Attack
When the same nonce k is reused across multiple signatures, it creates a catastrophic vulnerability:
Same r value: Since
r = g^k mod p, reusingkmeansris identical for all signaturesPrivate key recovery: With two signatures using the same
k, we can algebraically recover the private keyx
Exploitation Strategy
Step 1: Gather Information
Connect to the server and collect:
Public parameters:
p,g,yThe leaked nonce:
kTwo signatures for different messages
nc challs.nusgreyhats.org 33301Server Response:
Welcome to my super uwu secure digital signature scheme!
p = 28581604583782202143265111259124764526558825270793277405217394025745721954242254650965121482666635832686847474953440562605929002515608632964837704031786219117086986365555362911719993384506358582903300968928565193358567967799661175969775606231196220739601603275247187365872493703245095030170642525169857628874828723382454145699792436618477569467807268534404279857152331863245576074611134202405776998737108819578147772994298861844565336899724017862013870691248091221349545431949433843727531476934363372637337251401879263333278436051314996981429686780914130059682966220700794805365732309015888301565442824558714505349489
g = 13848683002866283304209230121718892686269072577134567400452720860245681493741003987303504800213923101669655981090103435680450277015337930888533692866570802369537467886393333681165721258219015658594810630182725352119309472177339329735747203066180815430324751872948679966917249314441187467096565697027183234785608229694988037776123692371541682254030402082934059532466644987165296295825826226378671369828451384797539559636704831402164768674918167354495036431879392891664896354939497263573261920042749726303930408770608926284688360604485321985811528949325116958202049524152355055092646825544117981593937928138981075791223
y = 20620169414902502338561831043406834260669101419380957375831823709489932725977581086850101378562777184663305809842090976763335619439956329768332032585950846586128220779762547145229109343686448381511459326285055126052695966813631202649358091081019604722000850119692422587396721259380786519957209785366924157968306737114724340278377608428009589963314349733766724993357830529725442599594445808471075513963148631428872496358353831004734925663817818703201066713221688729827656326815768023582497197173724446141598150808611330560778011886157386166278670237679175086231949521056132411275362078398718795815943470282645224905094
k: 4378031792006870256693045691209051164960988969025829825114846073833994185874539390707268303609688979239078737994155476188447496452455468267915199597825087188244546679257572153774462208881950968258828157789999407319488829138213142408315712593063525712267189620037760486656749699518895440553202291352655671196912061703817108922077335853601805172639293529253815481660739980616407018577925423407050396812598034367384127990699382151869423433592672208876788106391055466824446030188868505296175820029016572299301915661367806433440549210008514916434938928457069975985280022388868960383859028408943033402336677527171992082595Step 2: Obtain Known Signatures
Request signatures for two different messages:
# Message 1
{"m": 123}
# Response: s1 = 23838810343301920651199855403738533075669127243241581832260507739861908376432660810729411684162925085875999308387624191042729204056846442735425178382059835387510274927702225838598803861139822388686585693807811689773927870039425486836041691046772329959964553085685452383946495857597640443798158514729231448385624894266551590000834025287115178659464093773267188885476938114971610159256437861800918775540646007440489259455011992824064027289155379958227279102393436021561510509359628056861623928013854067045704383991867682459244759009895348825096582933270608574940647895786833274899468859842999207095286566827263306607389
# Message 2
{"m": 456}
# Response: s2 = 28360411703274409235364904101035918833158994677565402586787423202924288819877593049790719717633928779973618481431553628399167407272985931193312758063166875933167739272573429490552005807764105944661193640929020751464393592423653878125662081288481061162771378775400590880896808285267122572121780363268371459516358018196542205223830367020924644288141387041252216534005370718969934165312795253299827988547950751354276199823892769338600092450259308001059597630657632560145526065991943396768248849401440724037420338562058837975202553263435383927133297147258151388521932151203329935203733506100854105909132007693117738599043Step 3: Recover Private Key
With the leaked k and known signatures, we can recover the private key x:
from Crypto.Util.number import *
import hashlib
# Server parameters
p = 28581604583782202143265111259124764526558825270793277405217394025745721954242254650965121482666635832686847474953440562605929002515608632964837704031786219117086986365555362911719993384506358582903300968928565193358567967799661175969775606231196220739601603275247187365872493703245095030170642525169857628874828723382454145699792436618477569467807268534404279857152331863245576074611134202405776998737108819578147772994298861844565336899724017862013870691248091221349545431949433843727531476934363372637337251401879263333278436051314996981429686780914130059682966220700794805365732309015888301565442824558714505349489
g = 13848683002866283304209230121718892686269072577134567400452720860245681493741003987303504800213923101669655981090103435680450277015337930888533692866570802369537467886393333681165721258219015658594810630182725352119309472177339329735747203066180815430324751872948679966917249314441187467096565697027183234785608229694988037776123692371541682254030402082934059532466644987165296295825826226378671369828451384797539559636704831402164768674918167354495036431879392891664896354939497263573261920042749726303930408770608926284688360604485321985811528949325116958202049524152355055092646825544117981593937928138981075791223
y = 20620169414902502338561831043406834260669101419380957375831823709489932725977581086850101378562777184663305809842090976763335619439956329768332032585950846586128220779762547145229109343686448381511459326285055126052695966813631202649358091081019604722000850119692422587396721259380786519957209785366924157968306737114724340278377608428009589963314349733766724993357830529725442599594445808471075513963148631428872496358353831004734925663817818703201066713221688729827656326815768023582497197173724446141598150808611330560778011886157386166278670237679175086231949521056132411275362078398718795815943470282645224905094
k = 4378031792006870256693045691209051164960988969025829825114846073833994185874539390707268303609688979239078737994155476188447496452455468267915199597825087188244546679257572153774462208881950968258828157789999407319488829138213142408315712593063525712267189620037760486656749699518895440553202291352655671196912061703817108922077335853601805172639293529253815481660739980616407018577925423407050396812598034367384127990699382151869423433592672208876788106391055466824446030188868505296175820029016572299301915661367806433440549210008514916434938928457069975985280022388868960383859028408943033402336677527171992082595
# Known signatures
m1, s1 = 123, 23838810343301920651199855403738533075669127243241581832260507739861908376432660810729411684162925085875999308387624191042729204056846442735425178382059835387510274927702225838598803861139822388686585693807811689773927870039425486836041691046772329959964553085685452383946495857597640443798158514729231448385624894266551590000834025287115178659464093773267188885476938114971610159256437861800918775540646007440489259455011992824064027289155379958227279102393436021561510509359628056861623928013854067045704383991867682459244759009895348825096582933270608574940647895786833274899468859842999207095286566827263306607389
# Calculate r and hash
r = pow(g, k, p)
def hash_m(m):
sha = hashlib.sha256()
sha.update(long_to_bytes(m))
return bytes_to_long(sha.digest())
h1 = hash_m(m1)
# Recover private key x
# Since GCD(r, p-1) = 1 in this case, we can directly invert r
x = ((h1 - s1 * k) * pow(r, -1, p-1)) % (p-1)Step 4: Forge Target Signature
Now we can forge a signature for the target message:
# Target message
target_msg = bytes_to_long(b"gib flag pls uwu")
h_target = hash_m(target_msg)
# Forge signature
s_target = ((h_target - x * r) * pow(k, -1, p-1)) % (p-1)
# Verify our forged signature
def verify_signature(m, r, s):
h = hash_m(m)
lhs = pow(g, h, p)
rhs = (pow(y, r, p) * pow(r, s, p)) % p
return lhs == rhs
print(f"Signature verification: {verify_signature(target_msg, r, s_target)}")
# Output: True ✅Step 5: Submit and Get Flag
Submit the forged signature:
{
"m": 137457664979133345092444721504268416885,
"r": 27705931485116418224621449487391072526860100540050399693307990203460680459828553263274088376514832048851321498318256595918064281644626554908004073462528866428742545798551092821132502292156020335628144976944494540262181952896725203942043453217739426882695841928469150760751996001249499160218181301695056131924497666938146481232527939976609433224719911634330650675041017384389020720071211278305737374659529203063748439620320606937581641575361683761134450635663507478270602360366008948007134200059007079880263541821727495791501426712965964048114432966289537884690939472913933704414600851982464515831750560784038339323329,
"s": 3543207270597413962505716531415114132702704596191944661388003774954873338098139925111296392184454136317632828016556975939555280062159887394254315487526931503835042187137742368311623145625866829766860101886078078984840736219946310485393489414826792986906492433895987309583910457710582884476607479763417049436296095883327367506177002685301362176116236256353590655991857985486594723491019327224146044798457822902498064857117764678755493714581719044483324058041922758141837238283034734729432543522092801081882116239603156573687007663234700890490163542581548168411381128535228379535239916453897728055539474657252133721240
}Server Response:
Very cutesy, very mindful, very demure!
grey{h_h_H_h0wd_y0u_Do_tH4T_OMO}Flag
grey{h_h_H_h0wd_y0u_Do_tH4T_OMO}
Key Takeaways
Never reuse nonces in cryptographic schemes - it's catastrophic for security
Never leak private values like nonces, even "accidentally"
K-reuse attacks are a classic vulnerability in signature schemes (DSA, ECDSA, etc.)
Proper implementation should generate a fresh, random nonce for each signature
Defense in depth - even if nonces were properly generated, leaking them would still be fatal
This challenge demonstrates why cryptographic implementations must be extremely careful about nonce generation and keeping secrets truly secret!
Last updated
Was this helpful?