[{"data":1,"prerenderedAt":1310},["ShallowReactive",2],{"content:\u002Flitctf2026-partial-wp":3,"surround:\u002Flitctf2026-partial-wp":1299},{"id":4,"title":5,"body":6,"categories":1273,"date":1275,"description":1276,"draft":1277,"extension":1278,"image":1279,"meta":1280,"navigation":1282,"path":1283,"permalink":1283,"published":1279,"readingTime":1284,"recommend":1279,"references":1279,"seo":1289,"sitemap":1290,"stem":1291,"tags":1292,"type":1297,"updated":1275,"__hash__":1298},"content\u002Fposts\u002F2026\u002Flitctf2026-partial-wp.md","LitCTF 2026 · 部分题解",{"type":7,"value":8,"toc":1205},"minimark",[9,14,23,27,30,34,38,48,51,59,77,86,89,94,105,115,119,122,128,131,137,140,146,150,158,165,169,175,178,189,191,195,198,210,213,216,222,225,231,234,253,256,262,265,273,276,296,298,302,308,315,317,321,324,335,338,345,348,354,357,363,366,380,383,388,390,394,397,408,411,418,426,429,432,435,441,444,455,458,463,465,469,472,480,483,490,493,509,519,522,556,559,565,568,584,587,592,594,598,601,608,611,614,621,644,651,657,660,675,678,683,685,689,692,700,703,706,717,725,728,750,753,756,762,771,774,778,783,786,792,794,798,801,809,812,815,829,832,838,841,847,850,855,858,861,872,875,881,883,886,892,912,915,917,921,927,931,938,944,949,967,973,979,985,991,993,997,1002,1007,1012,1017,1023,1029,1034,1036,1040,1045,1054,1059,1064,1070,1076,1079,1084,1086,1090,1095,1104,1111,1116,1122,1128,1133,1135,1138],[10,11,13],"h2",{"id":12},"litctf2026_web_wp","LitCTF2026_WEB_WP",[15,16,18,22],"h1",{"id":17},"litctf2026-web-wp",[19,20,21],"span",{},"LitCTF2026"," Web WP",[24,25,26],"p",{},"本文整理 LitCTF2026 的两道 Web 题，重点保留利用链、关键泄露点和可复现步骤，方便直接发布到博客。",[28,29],"hr",{},[10,31,33],{"id":32},"_1-northbridge-document-hub","1. Northbridge Document Hub",[35,36,37],"h3",{"id":37},"题目信息",[39,40,41,45],"ul",{},[42,43,44],"li",{},"分类：Web",[42,46,47],{},"题面摘要：文档中心接入了 kkFileView 兼容预览网关，研究员账号已开放，目标是从解析缓存里拿到财务归档中的 flag。",[35,49,50],{"id":50},"核心思路",[24,52,53,54,58],{},"这是一个典型的“先拿普通账号，再读缓存文件”的题。反编译 ",[55,56,57],"code",{"code":57},"northbridge_ROOT.war"," 后，可以直接看到两条关键信息：",[39,60,61,67],{},[42,62,63,64],{},"登录凭据是硬编码的：",[55,65,66],{"code":66},"researcher \u002F Research#2026",[42,68,69,70,73,74],{},"文件下载接口是 ",[55,71,72],{"code":72},"\u002Fkkfileview\u002FgetCorsFile","，参数名为 ",[55,75,76],{"code":76},"urlPath",[24,78,79,81,82,85],{},[55,80,76],{"code":76}," 先做 Base64 解码，再交给路径解析器；如果解码后的路径不是缓存绝对路径，就会被拼到 ",[55,83,84],{"code":84},"\u002Fopt\u002Fkkfileview\u002Fcache\u002Fparsed"," 下面。",[35,87,88],{"id":88},"利用步骤",[90,91,93],"h4",{"id":92},"step-1-登录后台","Step 1: 登录后台",[24,95,96,97,100,101,104],{},"前端 ",[55,98,99],{"code":99},"portal.js"," 和后端 ",[55,102,103],{"code":103},"LoginServlet"," 都泄露了账号密码：",[106,107,113],"pre",{"className":108,"code":110,"language":111,"meta":112},[109],"language-text","researcher:Research#2026\n","text","",[55,114,110],{"__ignoreMap":112},[90,116,118],{"id":117},"step-2-定位目标文件","Step 2: 定位目标文件",[24,120,121],{},"Dashboard 里能看到审计日志：",[106,123,126],{"className":124,"code":125,"language":111,"meta":112},[109],"doc\u002Ffinance_2026q1.xlsx parse SUCCESS\n",[55,127,125],{"__ignoreMap":112},[24,129,130],{},"结合“本季度财务归档”的题意，目标文件名就是：",[106,132,135],{"className":133,"code":134,"language":111,"meta":112},[109],"q1_finance_report_2026.zip\n",[55,136,134],{"__ignoreMap":112},[24,138,139],{},"把它做 Base64：",[106,141,144],{"className":142,"code":143,"language":111,"meta":112},[109],"cTFfZmluYW5jZV9yZXBvcnRfMjAyNi56aXA=\n",[55,145,143],{"__ignoreMap":112},[90,147,149],{"id":148},"step-3-读取缓存文件","Step 3: 读取缓存文件",[106,151,156],{"className":152,"code":154,"language":155,"meta":112},[153],"language-bash","curl -b cookies.txt -o q1_finance_report_2026.zip \\\n  \"http:\u002F\u002Fchallenge.cyclens.tech:30720\u002Fkkfileview\u002FgetCorsFile?urlPath=cTFfZmluYW5jZV9yZXBvcnRfMjAyNi56aXA=\"\n","bash",[55,157,154],{"__ignoreMap":112},[24,159,160,161,164],{},"解压后读取 ",[55,162,163],{"code":163},"flag.txt"," 即可。",[35,166,168],{"id":167},"flag","Flag",[106,170,173],{"className":171,"code":172,"language":111,"meta":112},[109],"flag{44xcdrkv-wklf-4wj-8avx-axgvh0zjzvzc4}\n",[55,174,172],{"__ignoreMap":112},[35,176,177],{"id":177},"关键点",[39,179,180,183,186],{},[42,181,182],{},"前端 JS 直接泄露凭据和接口名",[42,184,185],{},"目标不在目录穿越，而在缓存目录拼接逻辑",[42,187,188],{},"只要猜对文件名，就能直接下载归档",[28,190],{},[10,192,194],{"id":193},"_2-lit_reverse_my_web","2. lit_reverse_my_web",[35,196,37],{"id":197},"题目信息-1",[39,199,200,203],{},[42,201,202],{},"分类：Web \u002F Reverse",[42,204,205,206,209],{},"题面摘要：需要逆出服务端逻辑，再伪造管理员身份拿 ",[55,207,208],{"code":208},"\u002Fflag","。",[35,211,50],{"id":212},"核心思路-1",[24,214,215],{},"这题的重点不是爆破 Web，而是逆服务端二进制。工作区里已经保留了现成利用脚本：",[106,217,220],{"className":218,"code":219,"language":111,"meta":112},[109],"lit_reverse_my_web\u002Fsolve.py\n",[55,221,219],{"__ignoreMap":112},[24,223,224],{},"脚本已经提取出 JWT 的 HS256 密钥：",[106,226,229],{"className":227,"code":228,"language":111,"meta":112},[109],"rMw_2026_litctf_jwt_secret_key!!\n",[55,230,228],{"__ignoreMap":112},[24,232,233],{},"利用方式很直接：",[235,236,237,244],"ol",{},[42,238,239,240,243],{},"伪造 ",[55,241,242],{"code":242},"role=admin"," 的 token",[42,245,246,247,250,251],{},"带 ",[55,248,249],{"code":249},"Authorization: Bearer \u003Ctoken>"," 请求 ",[55,252,208],{"code":208},[35,254,255],{"id":255},"现成脚本",[106,257,260],{"className":258,"code":259,"language":155,"meta":112},[153],"python lit_reverse_my_web\u002Fsolve.py http:\u002F\u002Fchallenge.cyclens.tech:30273\n",[55,261,259],{"__ignoreMap":112},[35,263,264],{"id":264},"关键字段",[106,266,271],{"className":267,"code":269,"language":270,"meta":112},[268],"language-json","{\n  \"role\": \"admin\",\n  \"iss\": \"reverseMyWeb\",\n  \"sub\": \"alice\"\n}\n","json",[55,272,269],{"__ignoreMap":112},[35,274,275],{"id":275},"本地证据",[39,277,278,284,290],{},[42,279,280,281],{},"题包：",[55,282,283],{"code":283},"lit_reverse_my_web\u002Fchallenge.zip",[42,285,286,287],{},"本地服务：",[55,288,289],{"code":289},"lit_reverse_my_web\u002Fsrc\u002Fserver.exe",[42,291,292,293],{},"利用脚本：",[55,294,295],{"code":295},"lit_reverse_my_web\u002Fsolve.py",[28,297],{},[10,299,301],{"id":300},"litctf2026_misc_wp","LitCTF2026_MISC_WP",[15,303,305,307],{"id":304},"litctf2026-misc-wp",[19,306,21],{}," Misc WP",[24,309,310,311,314],{},"本文整理 LitCTF2026 已完成的几道 ",[55,312,313],{"code":313},"Misc"," 题，保留题目思路、关键操作和最终结果，方便直接发布。",[28,316],{},[10,318,320],{"id":319},"_1-lit_lsb_base64","1. lit_lsb_base64",[35,322,323],{"id":323},"题目类型",[39,325,326,329,332],{},[42,327,328],{},"图片隐写",[42,330,331],{},"LSB",[42,333,334],{},"Base64",[35,336,337],{"id":337},"解题思路",[24,339,340,341,344],{},"题包中只有一张 ",[55,342,343],{"code":343},"stego.png","。先做基础排查后可以发现这张图非常像“整图作为载体”的 LSB 隐写题，于是直接提取各颜色通道的最低位。",[24,346,347],{},"实际测试后，红通道最低位按顺序取出、每 8 位拼成一个字节，就能在前面少量填充数据之后看到明显的 Base64：",[106,349,352],{"className":350,"code":351,"language":111,"meta":112},[109],"TGl0Q1RGe2xzYl8xc19mdW5fdzF0aF9iNHMzXzY0fQ==\n",[55,353,351],{"__ignoreMap":112},[24,355,356],{},"解码后得到：",[106,358,361],{"className":359,"code":360,"language":111,"meta":112},[109],"LitCTF{lsb_1s_fun_w1th_b4s3_64}\n",[55,362,360],{"__ignoreMap":112},[35,364,177],{"id":365},"关键点-1",[39,367,368,374,377],{},[42,369,370,371],{},"不要只盯着 ",[55,372,373],{"code":373},"strings",[42,375,376],{},"直接检查 RGB 三个通道的 LSB",[42,378,379],{},"提出来的字节流里如果出现长串可打印字符，优先怀疑 Base64",[35,381,168],{"id":382},"flag-1",[106,384,386],{"className":385,"code":360,"language":111,"meta":112},[109],[55,387,360],{"__ignoreMap":112},[28,389],{},[10,391,393],{"id":392},"_2-lit_rush_qr","2. lit_rush_qr",[35,395,323],{"id":396},"题目类型-1",[39,398,399,402,405],{},[42,400,401],{},"GIF",[42,403,404],{},"QR",[42,406,407],{},"图像恢复",[35,409,337],{"id":410},"解题思路-1",[24,412,413,414,417],{},"附件只有一个 ",[55,415,416],{"code":416},"rush.gif","，题面说“闪得很快”，并提示有人瞥见了二维码一角。先拆帧检查，发现一共 5 帧：",[39,419,420,423],{},[42,421,422],{},"第 0、1、3 帧是普通提示文字",[42,424,425],{},"第 2、4 帧是同一张黑白二维码图",[24,427,428],{},"也就是说，真正有用的不是“把多帧叠加”，而是中间那张二维码本身。",[24,430,431],{},"把二维码主体裁出来后可以看出：这是一张故意损坏的二维码，缺少了两个定位角。由于二维码本身纠错等级较高，可以尝试将标准 finder pattern 手工补回，再交给解码器识别。",[24,433,434],{},"将图像量化到模块网格后，补回左上、右上、左下三个标准定位框及分隔白边，最终成功解码：",[106,436,439],{"className":437,"code":438,"language":111,"meta":112},[109],"LitCTF{qr_h1gh_3rr_c0r_r3c0v3ry}\n",[55,440,438],{"__ignoreMap":112},[35,442,177],{"id":443},"关键点-2",[39,445,446,449,452],{},[42,447,448],{},"“闪得快”是误导，先拆帧再说",[42,450,451],{},"第 2\u002F4 帧完全相同，说明不需要复杂时序恢复",[42,453,454],{},"二维码如果缺角，先考虑手工补标准定位图案",[35,456,168],{"id":457},"flag-2",[106,459,461],{"className":460,"code":438,"language":111,"meta":112},[109],[55,462,438],{"__ignoreMap":112},[28,464],{},[10,466,468],{"id":467},"_3-lit_sstv","3. lit_sstv",[35,470,323],{"id":471},"题目类型-2",[39,473,474,477],{},[42,475,476],{},"音频隐写",[42,478,479],{},"SSTV",[35,481,337],{"id":482},"解题思路-2",[24,484,485,486,489],{},"题目给出的是一段 ",[55,487,488],{"code":488},"signal.wav","，听起来像调制解调器或短波噪声，典型 SSTV 风格。",[24,491,492],{},"先看音频参数：",[39,494,495,498,503],{},[42,496,497],{},"单声道",[42,499,500],{},[55,501,502],{"code":502},"44.1 kHz",[42,504,505,506],{},"时长约 ",[55,507,508],{"code":508},"115.2s",[24,510,511,512,515,516,518],{},"这个时长和 ",[55,513,514],{"code":514},"Martin M1"," 一整张图的发射时长高度吻合，因此优先按 ",[55,517,514],{"code":514}," 解码。",[24,520,521],{},"后续流程：",[235,523,524,527,550,553],{},[42,525,526],{},"对音频做解析，估计瞬时频率",[42,528,529,530,532,533],{},"按 ",[55,531,514],{"code":514}," 的行结构切分：\n",[39,534,535,538,541,544,547],{},[42,536,537],{},"sync",[42,539,540],{},"porch",[42,542,543],{},"G",[42,545,546],{},"B",[42,548,549],{},"R",[42,551,552],{},"初步重建整张图",[42,554,555],{},"再根据逐行同步脉冲做校正，修正行漂移",[24,557,558],{},"最终图中清楚显示：",[106,560,563],{"className":561,"code":562,"language":111,"meta":112},[109],"LitCTF{sstv_p4t13nc3}\n",[55,564,562],{"__ignoreMap":112},[35,566,177],{"id":567},"关键点-3",[39,569,570,578,581],{},[42,571,572,574,575,577],{},[55,573,508],{"code":508}," 基本是很强的 ",[55,576,514],{"code":514}," 特征",[42,579,580],{},"第一版图像哪怕有漂移，往往也足够暴露文字轮廓",[42,582,583],{},"逐行跟踪同步头可以明显提升最终可读性",[35,585,168],{"id":586},"flag-3",[106,588,590],{"className":589,"code":562,"language":111,"meta":112},[109],[55,591,562],{"__ignoreMap":112},[28,593],{},[10,595,597],{"id":596},"_4-lit_welcome","4. lit_welcome",[35,599,323],{"id":600},"题目类型-3",[39,602,603,605],{},[42,604,328],{},[42,606,607],{},"颜色通道差分",[35,609,337],{"id":610},"解题思路-3",[24,612,613],{},"题目说组委会发来一张“欢迎”图片，但肉眼看上去几乎是纯白。",[24,615,616,617,620],{},"查看 ",[55,618,619],{"code":619},"welcome.png"," 的像素统计后可以发现：",[39,622,623,629,634],{},[42,624,625,626],{},"绿色通道恒为 ",[55,627,628],{"code":628},"255",[42,630,631,632],{},"蓝色通道恒为 ",[55,633,628],{"code":628},[42,635,636,637,640,641,643],{},"红色通道只有 ",[55,638,639],{"code":639},"254"," 和 ",[55,642,628],{"code":628}," 两种值",[24,645,646,647,650],{},"这说明图里的内容不是不存在，而是被藏在“几乎纯白”的红通道里。把所有 ",[55,648,649],{"code":649},"R=254"," 的像素提取出来后，隐藏文字立刻显现，其中第二行直接给出 flag：",[106,652,655],{"className":653,"code":654,"language":111,"meta":112},[109],"LitCTF{w3lc0m3_t0_m1sc_w0rld}\n",[55,656,654],{"__ignoreMap":112},[35,658,177],{"id":659},"关键点-4",[39,661,662,665,668],{},[42,663,664],{},"白图不等于空图",[42,666,667],{},"先做通道统计，再做阈值分离",[42,669,670,671,674],{},"只差 ",[55,672,673],{"code":673},"1"," 的颜色值也足够藏信息",[35,676,168],{"id":677},"flag-4",[106,679,681],{"className":680,"code":654,"language":111,"meta":112},[109],[55,682,654],{"__ignoreMap":112},[28,684],{},[10,686,688],{"id":687},"_5-lit_pyjail_unicode","5. lit_pyjail_unicode",[35,690,323],{"id":691},"题目类型-4",[39,693,694,697],{},[42,695,696],{},"Pyjail",[42,698,699],{},"Unicode 标识符绕过",[35,701,337],{"id":702},"解题思路-4",[24,704,705],{},"源码核心逻辑如下：",[39,707,708,711,714],{},[42,709,710],{},"服务端收一行 Python",[42,712,713],{},"用正则黑名单检查原始源码文本",[42,715,716],{},"如果通过，就执行：",[106,718,723],{"className":719,"code":721,"language":722,"meta":112},[720],"language-python","eval(line, {\"__builtins__\": __builtins__})\n","python",[55,724,721],{"__ignoreMap":112},[24,726,727],{},"过滤规则只检查用户输入的原始字符串，比如：",[39,729,730,735,740,745],{},[42,731,732],{},[55,733,734],{"code":734},"open",[42,736,737],{},[55,738,739],{"code":739},"eval",[42,741,742],{},[55,743,744],{"code":744},"import",[42,746,747],{},[55,748,749],{"code":749},"__",[24,751,752],{},"但是 Python 在解析标识符时会做 Unicode 归一化，因此全角字符会被视为等价 ASCII 标识符。",[24,754,755],{},"例如：",[106,757,760],{"className":758,"code":759,"language":722,"meta":112},[720],"ｏｐｅｎ('\u002Fflag').read()\n",[55,761,759],{"__ignoreMap":112},[24,763,764,765,767,768,770],{},"对正则来说，这不是 ASCII 的 ",[55,766,734],{"code":734},"；但对 Python 解释器来说，它会被归一化并当成普通 ",[55,769,734],{"code":734}," 执行。",[24,772,773],{},"直接发送上面的 payload 即可读出 flag。",[35,775,777],{"id":776},"payload","Payload",[106,779,781],{"className":780,"code":759,"language":722,"meta":112},[720],[55,782,759],{"__ignoreMap":112},[35,784,168],{"id":785},"flag-5",[106,787,790],{"className":788,"code":789,"language":111,"meta":112},[109],"flag{lq9cghe6-8cco-4qk-8cti-h5esrd1xlvslu}\n",[55,791,789],{"__ignoreMap":112},[28,793],{},[10,795,797],{"id":796},"_6-lit_pyjail_reader","6. lit_pyjail_reader",[35,799,323],{"id":800},"题目类型-5",[39,802,803,806],{},[42,804,805],{},"Reader jail",[42,807,808],{},"交互式两步读文件",[35,810,337],{"id":811},"解题思路-5",[24,813,814],{},"这题根本不需要 RCE，题目源码已经把流程写得很清楚：",[235,816,817,820,826],{},[42,818,819],{},"先通过一个简单验证码",[42,821,822,823],{},"第一次读取 ",[55,824,825],{"code":825},"\u002Fapp\u002Fwhere_is_flag.txt",[42,827,828],{},"第二次读取上一步返回的真实 flag 路径",[24,830,831],{},"验证码是一个大写字符串，要求输入其逆序。通过后第一次读取：",[106,833,836],{"className":834,"code":835,"language":111,"meta":112},[109],"\u002Fapp\u002Fwhere_is_flag.txt\n",[55,837,835],{"__ignoreMap":112},[24,839,840],{},"服务端返回的内容是：",[106,842,845],{"className":843,"code":844,"language":111,"meta":112},[109],"\u002Fflag\n",[55,846,844],{"__ignoreMap":112},[24,848,849],{},"然后第二次再读取：",[106,851,853],{"className":852,"code":844,"language":111,"meta":112},[109],[55,854,844],{"__ignoreMap":112},[24,856,857],{},"即可得到 flag。",[35,859,177],{"id":860},"关键点-5",[39,862,863,866,869],{},[42,864,865],{},"这是“按提示读文件”的入门题，不要过度做成 RCE",[42,867,868],{},"验证码就是简单字符串反转",[42,870,871],{},"按题目要求分两步读取即可",[35,873,168],{"id":874},"flag-6",[106,876,879],{"className":877,"code":878,"language":111,"meta":112},[109],"flag{zpr4vjsv-iotj-4af-8dwg-kddegvu6viz09}\n",[55,880,878],{"__ignoreMap":112},[28,882],{},[10,884,885],{"id":885},"总结",[24,887,888,889,891],{},"这几道 ",[55,890,313],{"code":313}," 的整体风格偏入门和识别型，覆盖了几个很典型的方向：",[39,893,894,897,900,903,906,909],{},[42,895,896],{},"图片 LSB 隐写",[42,898,899],{},"近白色通道藏字",[42,901,902],{},"二维码修复",[42,904,905],{},"SSTV 音频转图像",[42,907,908],{},"Unicode Pyjail 绕过",[42,910,911],{},"按提示读取文件的 reader jail",[24,913,914],{},"如果后续继续整理，可以把每题对应的脚本单独放到附件中，形成“WP + solve script”的完整交付版本。",[28,916],{},[10,918,920],{"id":919},"litctf2026_crypto_wp","LitCTF2026_crypto_WP",[15,922,924,926],{"id":923},"litctf2026-crypto-wp",[19,925,21],{}," Crypto WP",[10,928,930],{"id":929},"_1-lit_xor_two_story-otp-key-reuse","1. lit_xor_two_story — OTP Key Reuse",[24,932,933,937],{},[934,935,936],"strong",{},"考点："," 流密码密钥复用攻击",[24,939,940,943],{},[934,941,942],{},"题目描述："," 同一串随机密钥流 k 加密了两条 40 字节明文，第二条明文已知。",[24,945,946],{},[934,947,948],{},"已知数据：",[39,950,951,956,961],{},[42,952,953],{},[55,954,955],{"code":955},"c1 = m1 XOR k",[42,957,958],{},[55,959,960],{"code":960},"c2 = m2 XOR k",[42,962,963,966],{},[55,964,965],{"code":965},"m2 = b\"litctf2026_xor_keystream_reuse_40bytes!!\"","（已知）",[24,968,969,972],{},[934,970,971],{},"解法："," XOR 两条密文消去密钥流，再与已知明文异或恢复 flag。",[106,974,977],{"className":975,"code":976,"language":111},[109],"c1 XOR c2 = (m1 XOR k) XOR (m2 XOR k) = m1 XOR m2\nm1 = (c1 XOR c2) XOR m2\n",[55,978,976],{"__ignoreMap":112},[106,980,983],{"className":981,"code":982,"language":722,"meta":112},[720],"c1 = bytes.fromhex('5f70a847ce12759e156e3cad1aa9530a119386a02ffc1c31bf14ab7a0a82ccc108f8476f75c98a28')\nc2 = bytes.fromhex('5f70a847ce123cc153283ca710ae7f042b8490a238eb2228970fad6a2694f2985dc5557e69e5f474')\nm2 = b'litctf2026_xor_keystream_reuse_40bytes!!'\nm1 = bytes(a ^ b ^ c for a, b, c in zip(c1, c2, m2))\n# litctf{otp_reuse_never_twice_same_key__}\n",[55,984,982],{"__ignoreMap":112},[24,986,987,990],{},[934,988,989],{},"教训："," OTP 每条密钥必须只使用一次。密钥流复用将两条密文的安全性降级为零——不需要密钥即可恢复双方明文。",[28,992],{},[10,994,996],{"id":995},"_2-lit_elgamal_handshake-elgamal-私钥泄露","2. lit_elgamal_handshake — ElGamal 私钥泄露",[24,998,999,1001],{},[934,1000,936],{}," ElGamal 加密 \u002F 调试信息泄露",[24,1003,1004,1006],{},[934,1005,942],{}," 服务端 debug 日志意外打印了 ElGamal 私钥 x。",[24,1008,1009,1011],{},[934,1010,948],{}," 公钥 (p, g, y)、密文 (c1, c2)、私钥 x。",[24,1013,1014,1016],{},[934,1015,971],{}," 正常 ElGamal 解密流程——已知私钥 x，直接计算共享秘密即可。",[106,1018,1021],{"className":1019,"code":1020,"language":111},[109],"s = c1^x mod p       # 共享秘密\nm = c2 * s^(-1) mod p # 恢复明文\n",[55,1022,1020],{"__ignoreMap":112},[106,1024,1027],{"className":1025,"code":1026,"language":722,"meta":112},[720],"from Crypto.Util.number import long_to_bytes\n\ns = pow(c1, x, p)\ns_inv = pow(s, -1, p)\nm = (c2 * s_inv) % p\nflag = long_to_bytes(m)\n# litctf{elgamal_leak_makes_happy_decrypt}\n",[55,1028,1026],{"__ignoreMap":112},[24,1030,1031,1033],{},[934,1032,989],{}," 私钥泄露 = 加密完全失效。生产环境绝不能将私钥、共享秘密等敏感数据写入日志。",[28,1035],{},[10,1037,1039],{"id":1038},"_3-lit_rsa_neighbor-rsa-fermat-分解","3. lit_rsa_neighbor — RSA Fermat 分解",[24,1041,1042,1044],{},[934,1043,936],{}," Fermat 分解 \u002F 临近素数漏洞",[24,1046,1047,1049,1050,1053],{},[934,1048,942],{}," 随机生成素数 p，连续调用 ",[55,1051,1052],{"code":1052},"next_prime()"," 若干次得到 q。p 和 q 间距极小。",[24,1055,1056,1058],{},[934,1057,948],{}," n, c, e = 65537。",[24,1060,1061,1063],{},[934,1062,971],{}," Fermat 分解适用于 |p - q| 较小的情况。",[106,1065,1068],{"className":1066,"code":1067,"language":111},[109],"设 a = (p+q)\u002F2, b = (p-q)\u002F2\n则 a^2 - n = b^2\n\n从 a = ceil(sqrt(n)) 开始，检查 a^2 - n 是否为完全平方数。\n一旦找到 b = sqrt(a^2 - n)，则 p = a-b, q = a+b。\n",[55,1069,1067],{"__ignoreMap":112},[106,1071,1074],{"className":1072,"code":1073,"language":722,"meta":112},[720],"import math\n\na = math.isqrt(n) + 1\nwhile True:\n    b2 = a * a - n\n    b = math.isqrt(b2)\n    if b * b == b2:\n        p = a - b\n        q = a + b\n        break\n    a += 1\n\nphi = (p - 1) * (q - 1)\nd = pow(e, -1, phi)\nm = pow(c, d, n)\n# litctf{rsa_fermat_finds_close_primes}\n",[55,1075,1073],{"__ignoreMap":112},[24,1077,1078],{},"第一轮迭代即命中，说明 p 和 q 极度接近。",[24,1080,1081,1083],{},[934,1082,989],{}," RSA 密钥生成必须确保 p 和 q 充分随机、相互独立，间距足够大才能抵抗 Fermat 分解。",[28,1085],{},[10,1087,1089],{"id":1088},"_4-lit_tiny_key_aes-aes-密钥空间过小","4. lit_tiny_key_aes — AES 密钥空间过小",[24,1091,1092,1094],{},[934,1093,936],{}," 密钥空间枚举 \u002F AES-ECB",[24,1096,1097,1099,1100,1103],{},[934,1098,942],{}," AES-128-ECB 密钥前 13 字节固定为 ",[55,1101,1102],{"code":1102},"LitCTF2026!!!","，仅末尾 3 字节随机。",[24,1105,1106,1108,1109,209],{},[934,1107,948],{}," 密文 (48 字节)、密钥前缀 ",[55,1110,1102],{"code":1102},[24,1112,1113,1115],{},[934,1114,971],{}," 未知密钥空间仅 2^24 ≈ 1677 万，可暴力枚举。",[106,1117,1120],{"className":1118,"code":1119,"language":722,"meta":112},[720],"from Crypto.Cipher import AES\nfrom Crypto.Util.Padding import unpad\n\nKEY_PREFIX = b\"LitCTF2026!!!\"\n\nfor b0 in range(256):\n    for b1 in range(256):\n        for b2 in range(256):\n            key = KEY_PREFIX + bytes([b0, b1, b2])\n            try:\n                cipher = AES.new(key, AES.MODE_ECB)\n                plain = unpad(cipher.decrypt(c), AES.block_size)\n                if plain.startswith(b'litctf{'):\n                    print(plain.decode())  # litctf{aes_tiny_brut3_for_the_win!}\n            except ValueError:\n                pass  # padding 不合法，跳过\n",[55,1121,1119],{"__ignoreMap":112},[24,1123,1124,1125,209],{},"pycryptodome C 扩展实现，1677 万次解密约十余秒完成。后缀为 ",[55,1126,1127],{"code":1127},"37a201",[24,1129,1130,1132],{},[934,1131,989],{}," AES-128 密钥必须全随机生成。部分固定的密钥相当于降级为超短密钥，彻底失去抗暴力破解能力。",[28,1134],{},[10,1136,885],{"id":1137},"总结-1",[1139,1140,1141,1157],"table",{},[1142,1143,1144],"thead",{},[1145,1146,1147,1151,1154],"tr",{},[1148,1149,1150],"th",{},"题目",[1148,1152,1153],{},"漏洞类型",[1148,1155,1156],{},"核心教训",[1158,1159,1160,1172,1183,1194],"tbody",{},[1145,1161,1162,1166,1169],{},[1163,1164,1165],"td",{},"lit_xor_two_story",[1163,1167,1168],{},"OTP 密钥复用",[1163,1170,1171],{},"流密码密钥绝不重复使用",[1145,1173,1174,1177,1180],{},[1163,1175,1176],{},"lit_elgamal_handshake",[1163,1178,1179],{},"私钥泄露",[1163,1181,1182],{},"敏感材料不入日志",[1145,1184,1185,1188,1191],{},[1163,1186,1187],{},"lit_rsa_neighbor",[1163,1189,1190],{},"临近素数",[1163,1192,1193],{},"p、q 必须独立随机且间距足够大",[1145,1195,1196,1199,1202],{},[1163,1197,1198],{},"lit_tiny_key_aes",[1163,1200,1201],{},"密钥空间过小",[1163,1203,1204],{},"密钥必须全随机生成",{"title":112,"searchDepth":1206,"depth":1206,"links":1207},4,[1208,1210,1222,1229,1230,1236,1242,1248,1254,1260,1266,1267,1268,1269,1270,1271,1272],{"id":12,"depth":1209,"text":13},2,{"id":32,"depth":1209,"text":33,"children":1211},[1212,1214,1215,1220,1221],{"id":37,"depth":1213,"text":37},3,{"id":50,"depth":1213,"text":50},{"id":88,"depth":1213,"text":88,"children":1216},[1217,1218,1219],{"id":92,"depth":1206,"text":93},{"id":117,"depth":1206,"text":118},{"id":148,"depth":1206,"text":149},{"id":167,"depth":1213,"text":168},{"id":177,"depth":1213,"text":177},{"id":193,"depth":1209,"text":194,"children":1223},[1224,1225,1226,1227,1228],{"id":197,"depth":1213,"text":37},{"id":212,"depth":1213,"text":50},{"id":255,"depth":1213,"text":255},{"id":264,"depth":1213,"text":264},{"id":275,"depth":1213,"text":275},{"id":300,"depth":1209,"text":301},{"id":319,"depth":1209,"text":320,"children":1231},[1232,1233,1234,1235],{"id":323,"depth":1213,"text":323},{"id":337,"depth":1213,"text":337},{"id":365,"depth":1213,"text":177},{"id":382,"depth":1213,"text":168},{"id":392,"depth":1209,"text":393,"children":1237},[1238,1239,1240,1241],{"id":396,"depth":1213,"text":323},{"id":410,"depth":1213,"text":337},{"id":443,"depth":1213,"text":177},{"id":457,"depth":1213,"text":168},{"id":467,"depth":1209,"text":468,"children":1243},[1244,1245,1246,1247],{"id":471,"depth":1213,"text":323},{"id":482,"depth":1213,"text":337},{"id":567,"depth":1213,"text":177},{"id":586,"depth":1213,"text":168},{"id":596,"depth":1209,"text":597,"children":1249},[1250,1251,1252,1253],{"id":600,"depth":1213,"text":323},{"id":610,"depth":1213,"text":337},{"id":659,"depth":1213,"text":177},{"id":677,"depth":1213,"text":168},{"id":687,"depth":1209,"text":688,"children":1255},[1256,1257,1258,1259],{"id":691,"depth":1213,"text":323},{"id":702,"depth":1213,"text":337},{"id":776,"depth":1213,"text":777},{"id":785,"depth":1213,"text":168},{"id":796,"depth":1209,"text":797,"children":1261},[1262,1263,1264,1265],{"id":800,"depth":1213,"text":323},{"id":811,"depth":1213,"text":337},{"id":860,"depth":1213,"text":177},{"id":874,"depth":1213,"text":168},{"id":885,"depth":1209,"text":885},{"id":919,"depth":1209,"text":920},{"id":929,"depth":1209,"text":930},{"id":995,"depth":1209,"text":996},{"id":1038,"depth":1209,"text":1039},{"id":1088,"depth":1209,"text":1089},{"id":1137,"depth":1209,"text":885},[1274],"Practice","2026-05-23","汇总 LitCTF 2026 的 Web、Misc、Crypto 部分题解，按题型分段整理。",false,"md",null,{"slots":1281},{},true,"\u002Flitctf2026-partial-wp",{"text":1285,"minutes":1286,"time":1287,"words":1288},"13 min read",12.665,759900,2533,{"title":5,"description":1276},{"loc":1283},"posts\u002F2026\u002Flitctf2026-partial-wp",[1293,1294,313,1295,1296],"LitCTF","Web","Crypto","Writeup","tech","kWzq-gv6dqqSRIf9OORNo1-H_uKbkCIhUl2wiTTni4k",[1300,1305],{"title":1301,"path":1302,"stem":1303,"date":1304,"type":1297,"children":-1},"5 月 21 日刷题记录","\u002F5月21日刷题","posts\u002F2026\u002F5月21日刷题","2026-05-21",{"title":1306,"path":1307,"stem":1308,"date":1309,"type":1297,"children":-1},"Hack The Box · Misc 两题速记","\u002Fhackthebox","posts\u002F2026\u002Fhackthebox","2026-05-27",1786294717695]