原来所有诱饵路径都回同一个 WordPress 登录页。爆破工具的标准流程是 「先按路径判断这是什么后台,再拿它认识的字段名填表」——在 /phpmyadmin/index.php 上回 WP 表单,等于告诉对方「这里没有 PMA」,对方直接换目标,既拿不到凭据 也没有取证价值。 新增 family.go(皮肤表)+ asset.go(静态资源占位),page.go 改为按家族渲染: wordpress /wp-login.php /wp-admin /wp-json log/pwd、wordpress_test_cookie phpmyadmin /phpmyadmin /pma /adminer.php pma_username/pma_password、hidden token tomcat /manager/html /host-manager 401 + WWW-Authenticate: Basic(无表单) jenkins /jenkins /jnlpJars j_username/j_password、X-Jenkins grafana /grafana user/password、深色主题 gitlab /users/sign_in user[login]/user[password] luci /cgi-bin/luci /luci luci_username/luci_password joomla /administrator username/passwd、hidden option=com_login generic /admin /console /zabbix /jira… username/password Tomcat 刻意不给 HTML 表单:真实 Manager 未认证就是 401 + Basic 弹框,给表单 反而更假;Authorization: Basic 本就在 ExtractCredentials 覆盖范围内,捕获能力不减。 凭据字段: - credentialKeys 扩 pma_* / j_* / luci_* / user[login] / user[password]; - token 只用于判 HadFields,**移出 isPasswordKey** —— PMA/GitLab 的登录表单带 hidden CSRF token,而 Fields 是 map、遍历无序,原来的实现会让 token 随机 顶掉真密码,记录下来的「密码」时对时错。 资源占用(这是本次主要约束): - 渲染一页 3.7µs / 2 次分配 / 5.2KB(改前 12µs / 22 次 / 19.5KB): 不用 html/template(每次 ParseFS 是毫秒级 IO+编译),改单次 strings.Builder 并避免 "a"+b+"c" 反复分配,Replacer 提到包级; - 静态资源请求 55ns / 0 分配:CSS/JS 回常量片段、图片回真 1×1 PNG, **不解析 IP、不判封禁、不写库**——真实后台页一次加载 5–10 个资源请求, 否则等于把页面浏览放大成十几条 INSERT。插件目录下的 .php 回 404(webshell 探测)。 honeypot paths 增加家族汇总,便于确认实际生效的分布。 新增 dev/preview-honeypot:把 9 个家族渲染到 outputs/honeypot-preview/index.html 再配 headless 截图核对外观——本轮两个「源码看不出、截图一眼假」的问题 (Tomcat 忘配 css 导致按钮白字无底色、WP 登录页底部挂着 404 文案)都是它发现的。 测试:TestFamilyOf / TestEveryFamilyRendersItsOwnFields / TestTomcatUses401Basic / TestAssetFor / TestRenderIsCheap(4KB 上限)/ BenchmarkRenderFamily / BenchmarkAssetFor,route 层 TestHoneypotServesFamilySpecificPage / TestHoneypotCapturesFamilyFields / TestHoneypotAssetRequestsAreNotRecorded。 变异验证:资源短路失效 → 3 个资源全变 text/html 且落库;FamilyOf 恒返回 Generic → 页面缺特征字段。两项均按预期变红。
78 lines
3.0 KiB
Go
78 lines
3.0 KiB
Go
// Command preview-honeypot 把全部伪装家族渲染成静态 HTML,供人工核对外观。
|
||
//
|
||
// 用法:go run ./dev/preview-honeypot
|
||
// 产物:outputs/honeypot-preview/index.html(9 个 iframe 并排)
|
||
//
|
||
// 改动 internal/security/honeypot/family.go 的皮肤后重新跑一次,
|
||
// 比对着源码想象 CSS 可靠得多 —— 上一版就是靠它发现 Tomcat 按钮没背景色。
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/tamakyi/TamaBox/internal/security/honeypot"
|
||
)
|
||
|
||
type item struct {
|
||
fam honeypot.Family
|
||
action string
|
||
note string
|
||
}
|
||
|
||
func main() {
|
||
dir := "outputs/honeypot-preview"
|
||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||
panic(err)
|
||
}
|
||
items := []item{
|
||
{honeypot.FamilyWordPress, "/wp-login.php", "/wp-login.php · /wp-admin"},
|
||
{honeypot.FamilyPhpMyAdmin, "/phpmyadmin/index.php", "/phpmyadmin · /pma"},
|
||
{honeypot.FamilyTomcat, "/manager/html", "/manager/html · 401 Basic"},
|
||
{honeypot.FamilyJenkins, "/jenkins/login", "/jenkins"},
|
||
{honeypot.FamilyGrafana, "/grafana/login", "/grafana"},
|
||
{honeypot.FamilyGitLab, "/users/sign_in", "/users/sign_in"},
|
||
{honeypot.FamilyLuCI, "/cgi-bin/luci", "/cgi-bin/luci"},
|
||
{honeypot.FamilyJoomla, "/administrator/index.php", "/administrator"},
|
||
{honeypot.FamilyGeneric, "/admin", "/admin · /console"},
|
||
}
|
||
|
||
var cards strings.Builder
|
||
for _, it := range items {
|
||
name := it.fam.String()
|
||
body := honeypot.RenderFamily(it.fam, honeypot.LoginPageVars{
|
||
Action: it.action,
|
||
Redir: "/wp-admin/",
|
||
Failed: false,
|
||
})
|
||
if err := os.WriteFile(filepath.Join(dir, name+".html"), body, 0o644); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Fprintf(&cards, `<figure><figcaption><b>%s</b><span>%s</span></figcaption><iframe src="%s.html" title="%s"></iframe></figure>`,
|
||
name, it.note, name, name)
|
||
}
|
||
|
||
page := `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
|
||
<title>蜜罐伪装家族预览</title>
|
||
<style>
|
||
body{margin:0;padding:28px 24px 40px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f6f7f9;color:#1d2327}
|
||
h1{font-size:18px;font-weight:600;margin:0 0 6px}
|
||
p.lead{margin:0 0 22px;font-size:13px;color:#646970}
|
||
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:18px}
|
||
figure{margin:0;background:#fff;border:1px solid #dcdcde;border-radius:8px;overflow:hidden}
|
||
figcaption{display:flex;justify-content:space-between;gap:8px;padding:9px 12px;font-size:12px;border-bottom:1px solid #f0f0f1}
|
||
figcaption span{color:#787c82}
|
||
iframe{width:100%;height:520px;border:0;display:block}
|
||
</style></head><body>
|
||
<h1>蜜罐:9 个伪装家族</h1>
|
||
<p class="lead">每个 iframe 是 /wp-admin 这类诱饵路径的真实响应(同一份 RenderFamily 输出,未做任何美化)。Tomcat 家族在真实响应里是 401 + Basic Auth,这里有页面是为了看内容。</p>
|
||
<div class="grid">` + cards.String() + `</div></body></html>`
|
||
|
||
if err := os.WriteFile(filepath.Join(dir, "index.html"), []byte(page), 0o644); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Println("预览已生成:", filepath.Join(dir, "index.html"))
|
||
}
|