V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
horsley
V2EX  ›  分享创造

ocserv auth hacking

  •  
  •   horsley ·
    horsley · 2015-05-31 13:51:45 +08:00 · 4948 次点击
    这是一个创建于 3535 天前的主题,其中的信息可能已经有所发展或是发生改变。
    不知道其他跟我一样使用ocserv(anyconnect)的同学有没有这样一个疑惑
    在移动端anyconnect没办法保存密码,每次都需要手工输入
    而且用户名密码还要分两次提示输入

    研究了一下认证过程,目前写了一个小的hack:
    第一:hack认证表单,让用户名密码在一次提示中输入,并能正常认证使用
    第二:这是重点!anyconnect移动端是支持anyconnect这个scheme唤起并且传参,预填充用户名密码

    那么,我么可以把这个唤起的URL放到其他地方,例如说一个可以保存密码的,带有鉴权的web页面上。而且这个东西也可以把鉴权引出来做些别的东西,例如一次性密码什么的。

    由于mitm 所以存在内存拷贝次数加倍的问题。gist中只是展示了思路,后面再完善。

    // ocservFront project main.go
    package main
    import (
    "bufio"
    "bytes"
    "crypto/tls"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "os"
    "regexp"
    "strings"
    )
    const authFormOld = `<?xml version="1.0" encoding="UTF-8"?>
    <config-auth client="vpn" type="auth-request">
    <version who="sg">0.1(1)</version>
    <auth id="main">
    <message>Please enter your username</message>
    <form method="post" action="/auth">
    <input type="text" name="username" label="Username:" />
    </form></auth>
    </config-auth>`
    const authFormNew = `<?xml version="1.0" encoding="UTF-8"?>
    <config-auth client="vpn" type="auth-request">
    <version who="sg">0.1(1)</version>
    <auth id="main">
    <message>Please enter your username</message>
    <form method="post" action="/auth">
    <input type="text" name="username" label="Username:" />
    <input type="password" name="password" label="Password:" />
    </form></auth>
    </config-auth>`
    var authUserPassRE = regexp.MustCompile("<auth><username>(.*?)</username><password>(.*?)</password></auth>")
    var vpnCookieRE = regexp.MustCompile("webvpncontext=(.*?);")
    func main() {
    //@todo load config from file
    cert, _ := tls.LoadX509KeyPair("/Users/horsley/Backup/ssl/ihorsley.com.crt", "/Users/horsley/Backup/ssl/ihorsley.com.key")
    var cfg tls.Config
    cfg.Certificates = []tls.Certificate{cert}
    l, err := tls.Listen("tcp", ":15443", &cfg) //@todo load config from file
    if err != nil {
    log.Println("listen error:", err)
    os.Exit(1)
    }
    defer l.Close()
    for {
    conn, err := l.Accept()
    if err != nil {
    log.Println("accept error:", err)
    continue
    }
    go func(clientConn net.Conn) {
    log.Println("conn process start")
    defer clientConn.Close()
    //@todo load config from file
    remoteConn, err := tls.Dial("tcp", "vps9.ihorsley.com:443", nil) //remote can be unix cleartext socket of ocserv
    if err != nil {
    log.Println("connect upstream error:", err)
    return
    }
    defer remoteConn.Close()
    vpnCookie := make(chan string, 1)
    go func() { //remote => client
    hackRemoteResponse(clientConn, remoteConn, vpnCookie)
    io.Copy(clientConn, remoteConn)
    }()
    //client => remote
    hackClientRequest(clientConn, remoteConn, vpnCookie)
    io.Copy(remoteConn, clientConn)
    log.Println("conn process done")
    }(conn)
    }
    }
    //hackClientRequest
    //hack auth by resend both user and pass form and cookie
    func hackClientRequest(clientConn, remoteConn net.Conn, vpnCookie chan string) {
    reader := bufio.NewReader(clientConn)
    for { //client req hacking
    req, err := http.ReadRequest(reader)
    if err != nil {
    log.Println("ReadRequest from client conn err:", err)
    return
    }
    if req.URL.Path == "/auth" {
    var buf bytes.Buffer
    _, err = buf.ReadFrom(req.Body)
    req.Body.Close()
    if err != nil {
    log.Println("read request body from client conn err:", err)
    return
    }
    req.Body = ioutil.NopCloser(bytes.NewReader(buf.Bytes()))
    log.Println("3. second req, auth req, do first sending")
    req.Write(remoteConn)
    //waiting for cookie
    ck := <-vpnCookie
    log.Println("5. got cookie, resend auth req with cookie")
    req.Header.Set("Cookie", fmt.Sprintf("webvpncontext=%s;", ck))
    req.Body = ioutil.NopCloser(bytes.NewReader(buf.Bytes()))
    req.Write(remoteConn)
    log.Println("6. resend done, hack finish, going to do regular io.Copy")
    break
    } else {
    log.Println("1. first req, pass")
    req.Write(remoteConn)
    }
    }
    }
    //hackRemoteResponse
    //hack auth by change auth form and drop the first cookie resp
    func hackRemoteResponse(clientConn, remoteConn net.Conn, vpnCookie chan string) {
    reader := bufio.NewReader(remoteConn)
    for { //remote resp hacking
    resp, err := http.ReadResponse(reader, nil)
    if err != nil {
    log.Println("ReadResponse from remote conn err:", err)
    return
    }
    if resp.ContentLength == len(authFormOld) { //hack auth form
    log.Println("2. first resp, hack resp auth form")
    resp.Body.Close()
    resp.ContentLength = int64(len(authFormNew))
    resp.Body = ioutil.NopCloser(strings.NewReader(authFormNew))
    } else if ck := resp.Header.Get("Set-Cookie"); ck != "" && vpnCookieRE.MatchString(ck) { //get context cookie
    log.Println("4. second resp, grep cookie, resp hacking finish")
    if m := vpnCookieRE.FindStringSubmatch(ck); m != nil && m[1] != "" {
    vpnCookie <- m[1]
    //log.Println("get vpn cookie:", m[1])
    break
    }
    }
    err = resp.Write(clientConn)
    if err != nil {
    log.Println("write to client conn err:", err)
    return
    }
    }
    }
    view raw ocserv_hack.go hosted with ❤ by GitHub
    6 条回复    2015-06-03 17:33:08 +08:00
    Leafove
        1
    Leafove  
       2015-05-31 13:57:58 +08:00
    要不用输入密码直接换成证书验证就可以了
    horsley
        2
    horsley  
    OP
       2015-05-31 14:30:34 +08:00 via Android
    @Leafove 手机加客户端证书好像又要设置锁屏密码什么的,而且网上教程都是自签的做法,我用的是正规证书不知道怎么弄
    rwzsycwan
        3
    rwzsycwan  
       2015-05-31 16:05:12 +08:00
    @horsley 自己签一个证书用来验证
    server-cert 与server-key 填正规证书,底下还有一个ca-cert 填自己签的ca,
    Yien
        4
    Yien  
       2015-05-31 18:11:06 +08:00
    @rwzsycwan 好像用了證書就不能用radius了是嗎?
    rwzsycwan
        5
    rwzsycwan  
       2015-05-31 18:56:16 +08:00
    @Yien 额 没研究过radius
    bao3
        6
    bao3  
       2015-06-03 17:33:08 +08:00
    这个hack就不要浪费时间了,直接换成CertAuth方式,不需要radius。另外,android需要强制锁屏密码的问题,其实是你倒入证书的姿势不对或者apk不对。客户端倒入p12证书,android系统不会要求你设置PIN或者密码的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1736 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 01:08 · PVG 09:08 · LAX 17:08 · JFK 20:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.