miguan v1
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package honeypot
|
||||
|
||||
import (
|
||||
"html"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LoginPageVars 是假登录页的渲染参数。
|
||||
type LoginPageVars struct {
|
||||
// Action 是表单提交地址(当前诱饵路径本身)。
|
||||
Action string
|
||||
// Title / Heading 是页面标题与表单标题。
|
||||
Title string
|
||||
Heading string
|
||||
// Notice 是提交失败后的提示语。
|
||||
Notice string
|
||||
// Redir 为原始请求的查询串(WordPress 风格的 ?redirect_to=),原样回填。
|
||||
Redir string
|
||||
}
|
||||
|
||||
// RenderLoginPage 输出仿 WordPress 后台风格的假登录页(200,text/html)。
|
||||
//
|
||||
// 页面刻意包含:
|
||||
// - 隐藏 canary 标记(HTML 注释 + meta),便于日后溯源;
|
||||
// - 经典 WP 字段名 log / pwd / redirect_to —— 让暴力破解脚本原样提交,
|
||||
// 我们就能拿到最干净的用户名与密码对;
|
||||
// - 一个 nginx/Apache 风格的 404 页脚,暗示「这个后台真的存在」,
|
||||
// 但正文是纯静态 HTML,不泄露任何版本、路径或真实凭据。
|
||||
func RenderLoginPage(vars LoginPageVars) []byte {
|
||||
action := html.EscapeString(vars.Action)
|
||||
title := vars.Title
|
||||
if title == "" {
|
||||
title = "Log In ‹ WordPress"
|
||||
}
|
||||
heading := vars.Heading
|
||||
if heading == "" {
|
||||
heading = "WordPress"
|
||||
}
|
||||
notice := ""
|
||||
if vars.Notice != "" {
|
||||
notice = `<div id="login_error">` + html.EscapeString(vars.Notice) + `</div>`
|
||||
}
|
||||
redir := html.EscapeString(vars.Redir)
|
||||
|
||||
var b strings.Builder
|
||||
b.Grow(4096)
|
||||
b.WriteString(`<!DOCTYPE html>` + "\n")
|
||||
b.WriteString(`<!-- canary: ` + CanaryToken + ` -->` + "\n")
|
||||
b.WriteString(`<html lang="en-US">` + "\n")
|
||||
b.WriteString(`<head>` + "\n")
|
||||
b.WriteString(`<meta charset="UTF-8" />` + "\n")
|
||||
b.WriteString(`<meta name="viewport" content="width=device-width, initial-scale=1" />` + "\n")
|
||||
b.WriteString(`<meta name="generator" content="TamaBox honeypot ` + CanaryToken + `" />` + "\n")
|
||||
b.WriteString(`<title>` + html.EscapeString(title) + `</title>` + "\n")
|
||||
// 静态内联样式,避免蜜罐依赖站点静态资源(也避免污染真实缓存)
|
||||
b.WriteString(`<style>` + honeypotCSS + `</style>` + "\n")
|
||||
b.WriteString(`</head>` + "\n")
|
||||
b.WriteString(`<body class="login">` + "\n")
|
||||
b.WriteString(`<div id="login">` + "\n")
|
||||
b.WriteString(`<h1><a href="` + action + `" title="` + html.EscapeString(heading) + `">` + html.EscapeString(heading) + `</a></h1>` + "\n")
|
||||
b.WriteString(notice + "\n")
|
||||
b.WriteString(`<form name="loginform" id="loginform" action="` + action + `" method="post" enctype="application/x-www-form-urlencoded">` + "\n")
|
||||
b.WriteString(`<p><label for="user_login">Username<br /><input type="text" name="log" id="user_login" class="input" value="" size="20" autocapitalize="off" autocomplete="username" /></label></p>` + "\n")
|
||||
b.WriteString(`<p><label for="user_pass">Password<br /><input type="password" name="pwd" id="user_pass" class="input" value="" size="20" autocomplete="current-password" /></label></p>` + "\n")
|
||||
b.WriteString(`<p class="forgetmenot"><input name="rememberme" type="checkbox" id="rememberme" value="forever" /> Remember Me</p>` + "\n")
|
||||
b.WriteString(`<p class="submit"><input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="Log In" /></p>` + "\n")
|
||||
b.WriteString(`<input type="hidden" name="redirect_to" value="` + redir + `" />` + "\n")
|
||||
b.WriteString(`<input type="hidden" name="testcookie" value="1" />` + "\n")
|
||||
b.WriteString(`</form>` + "\n")
|
||||
b.WriteString(`<p id="nav"><a href="` + action + `?action=lostpassword">Lost your password?</a> | <a href="` + action + `?action=register">Register</a></p>` + "\n")
|
||||
b.WriteString(`</div>` + "\n")
|
||||
b.WriteString(`<div id="footer"><p>` + html.EscapeString(NotFoundFooter) + `</p></div>` + "\n")
|
||||
b.WriteString(`</body>` + "\n")
|
||||
b.WriteString(`</html>` + "\n")
|
||||
return []byte(b.String())
|
||||
}
|
||||
|
||||
const honeypotCSS = `html{background:#f0f0f1}body{margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;color:#3c434a}
|
||||
#login{border:1px solid #dcdcde;background:#fff;width:320px;margin:5em auto;padding:26px 24px 46px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
||||
#login h1{text-align:center;margin:0 0 1em;font-size:24px;font-weight:400}
|
||||
#login h1 a{color:#444;text-decoration:none}
|
||||
#login form{margin-top:16px}
|
||||
#login label{display:block;font-size:14px}
|
||||
#login input[type=text],#login input[type=password]{width:100%;padding:5px 8px;border:1px solid #ddd;border-radius:3px;box-sizing:border-box;margin-top:4px}
|
||||
#login .button{width:100%;padding:7px 12px;background:#2271b1;border:1px solid #2271b1;border-radius:3px;color:#fff;cursor:pointer;font-size:13px}
|
||||
#login .submit{margin:16px 0 0}
|
||||
#login_error{border-left:4px solid #d63638;background:#fcf0f1;padding:10px 12px;margin:12px 0}
|
||||
#nav,.forgetmenot{font-size:13px;color:#50575e}
|
||||
#footer{text-align:center;color:#646970;font-size:12px;margin:24px auto;max-width:320px}
|
||||
`
|
||||
|
||||
// NotFoundFooter 是 404 假页脚,用于给扫描器「后台存在但资源不存在」的错觉。
|
||||
const NotFoundFooter = "The requested resource was not found on this server."
|
||||
|
||||
// LoginFailureNotice 是提交凭据后的统一提示(不区分「用户不存在」与「密码错误」,
|
||||
// 避免因响应差异让爆破脚本调整策略;同时这也是虚假后台「存在」的证据)。
|
||||
const LoginFailureNotice = "ERROR: Invalid username or password."
|
||||
|
||||
// WriteLoginPage 将假登录页写入响应。
|
||||
func WriteLoginPage(w http.ResponseWriter, vars LoginPageVars) {
|
||||
body := RenderLoginPage(vars)
|
||||
h := w.Header()
|
||||
h.Set("Content-Type", "text/html; charset=utf-8")
|
||||
h.Set("Cache-Control", "no-store, no-cache, must-revalidate")
|
||||
// 诱饵页面绝不进入任何共享/边缘缓存,也不被中间层改写。
|
||||
h.Set("X-Robots-Tag", "noindex, nofollow")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(body)
|
||||
}
|
||||
Reference in New Issue
Block a user