tcl-hacks

Check-in [23cb074f5b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:add socks tamper tool
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:23cb074f5be4b3b7547a7feef69b884c2ab3b795
User & Date: aspect 2018-05-03 12:46:29
Context
2018-05-03
13:08
experiments in iterators check-in: b7c919f21e user: aspect tags: trunk
12:46
add socks tamper tool check-in: 23cb074f5b user: aspect tags: trunk
12:43
inclusion demo for configtcl check-in: 31835ea8b4 user: aspect tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added socks/README.md.































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
## Simple socks5 proxy for tampering with TCP

I recommend using this with redsocks:  http://darkk.net.ru/redsocks

## redsocks/iptables setup

Consider the goal:

  * given a target machine `192.168.122.121`, we want to tamper with some traffic sent to it in order to exercise flaws or boundary conditions
  * it's easier to tamper with well-formed traffic generated by a program or library than to read enough RFCs to write our own implementation of the protocol
  * to avoid profusion of VMs and a complex orchestrated setup, we'd rather do this all in one host

Enter `redsocks`.  With `iptables`, `redsocks` is able to *transparently* forward selected traffic through a SOCKS5 proxy.  We can be selective about what traffic is forwarded through either standard IP/TCP layer rules or using fancy capabilities such as `-m owner`.

To make it all work in one host, we need to ensure that the proxy's own traffic will not be transparently proxied.  `-m owner` comes in particularly handy for this.


### Build redsocks

You will need `libevent-devel`, then just type `make`:

    sudo dnf -y install libevent-devel
    git checkout https://github.com/darkk/redsocks
    cd redsocks
    make


### Configure redsocks

Take the standard config and adjust only `ip =` and `port =` lines to point where `socks5.tcl` will be running:

    cp redsocks.conf.example redsocks.conf
    vi redsocks.conf

    ip = localhost;
    port = 1080;


### Sysadmin magic

Add a group for the proxy to run under:

    groupadd mysocks

If you know how to play with `firewalld`, do that.  If not, disable the jerk and insert some rules like the following:

    iptables -t nat -N REDSOCKS

    # don't mess with local traffic
    iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
    iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
    # don't mess with proxy traffic
    iptables -t nat -A REDSOCKS -m owner --gid-owner mysocks -j RETURN

    iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345

Now create a rule or rules to send the traffic you want through the proxy.

All traffic:

    iptables -t nat -A OUTPUT -p tcp -j REDSOCKS

Traffic to a particular dest:

    iptables -t nat -A OUTPUT -p tcp -d 192.168.122.121 -j REDSOCKS

Traffic from processes belonging to a particular gid:

    iptables -t nat -A OUTPUT -p tcp -m owner --gid-owner socksified -j REDSOCKS

## Configure socks5.tcl

Edit the `write` procs in `tamper_up` (client->server) and `tamper_down` (sever->client).  `return $data` is the empty tamper which does nothing.


## Run everything

Run redsocks as any user you like:

    ./redsocks

And socks5.tcl *as gid mysocks*:

    sg mysocks tclsh socks5.tcl

Watch the logs and enjoy as you do your tampering.  So much fun!


## Going Further

Making this truly transparent (source port too) would be cool, but that requires getting routing infrastructure involved .. probably easiest on a vm acting as a router using `IP_TRANSPARENT` (see ip(7) and redsocks/doc/balabit-TPROXY-README.txt), which requires `CAP_NET_ADMIN`.

Note that NFQUEUE userspace filters can not edit packets, only delay them and make routing decisions.

Doing this fast in C would be relatively easy.  A decent architecture would be able to stream pcap output, and take commands on a socket to set up *simple* re-writes.

Added socks/iptables.sh.























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11

iptables -t nat -F
iptables -F
iptables -t nat -X REDSOCKS
iptables -t nat -N REDSOCKS
# don't mess with proxy traffic
iptables -t nat -A REDSOCKS -m owner --gid-owner squid -j RETURN
iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345


iptables -t nat -A OUTPUT -p tcp -d 192.168.122.121 -j REDSOCKS

Added socks/socks5.tcl.













































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env tclsh
# socks5 is pretty simple - outbound TCP only


namespace eval socks5 {

    proc main {{port 1080}} {
        socket -server [callback accept socks5] $port
        puts "Listening on $port"
        vwait forever
    }

    proc accept {handler chan host port} {
        chan configure $chan -blocking 0
        set coname [namespace tail $handler]:[string map {: _} $host]:$port
        coroutine $coname $handler $chan $host $port
    }

    proc socks5 {chan caddr cport} {
        finally [list catch [list close $chan]]
        finally {log Disconnecting}
        log "New connection from $caddr $cport"
        chan configure $chan -translation binary -buffering none

        # authentication
        scan [read $chan 2] %c%c ver nmeth
        if {$ver != 5} return
        binary scan [read $chan $nmeth] c* meths
        if {0 in $meths} {                  ;# no authentication! yay
            puts -nonewline $chan \x05\x00      ;# success!
        } elseif {2 in $meths} {            ;# user/passwd
            scan [read $chan 2] %c%c ver len
            if {$ver != 1} return
            set username [read $chan $len]
            scan [read $chan 1] %c len
            set password [read $chan $len]
            log "Authenticating $username $password"
            puts -nonewline $chan \x05\x00      ;# success!
        } else {
            puts -nonewline $chan \x05\xff      ;# no supported methods
            return
        }

        # request
        scan [read $chan 4] %c%c%c%c ver cmd z atyp
        if {$ver != 5 || $z != 0} return

        if {$atyp == 1} {       ;# IPv4
            scan [read $chan 4] %c%c%c%c a b c d
            set dst $a.$b.$c.$d
        } elseif {$atyp == 3} { ;# hostname
            scan [read $chan 1] %c alen
            set dst [read chan $alen]
        } elseif {$atyp == 4} { ;# IPv6
            binary scan [read $chan 16] c* dst
            set dst [join $dst :]
        }

        binary scan [read $chan 2] Su dpt

        if {$cmd == 3} {        ;# UDP
            puts -nonewline $chan \5\7\0\3\0\0\0    ;# cmd not supported ":0"
            log "UDP not supported"
            return
        } elseif {$cmd == 2} {  ;# bind
            puts -nonewline $chan \5\7\0\3\0\0\0    ;# cmd not supported ":0"
            log "Bind not supported"
            return
        } elseif {$cmd == 1} {  ;# connect

            log "Connecting to $dst $dpt"

            set upchan [socket -async $dst $dpt]
            finally [list catch [list close $upchan]]

            yieldto chan event $upchan writable [info coroutine]
            chan event $upchan writable ""

            set err [chan configure $upchan -error]
            if {$err ne ""} {
                # FIXME: proper responses are better
                # 1 generic 2 notallowed
                # 3 netunreach 4 hostunreach
                # 5 connrefused 6 ttlexpired
                log "Error $err: responding ECONNREFUSED"
                puts -nonewline $chan \5\5\0\1\0\0\0    ;# connection refused
                return
            }

            lassign [chan configure $upchan -sockname] myaddr _ myport

            log "Connected via $myaddr $myport"

            if {[scan $myaddr %d.%d.%d.%d a b c d] == 4} {
                set myaddr [format %c%c%c%c%c 1 $a $b $c $d]  ;# IPv4
            } else {
                # pack an IPv6 address
                lassign [split $myaddr ::] a b
                set myaddr [lrepeat 16 0]
                set i -1
                foreach octet $a {
                    lset $myaddr [incr i] $octet
                }
                set i 16
                foreach octet [lreverse $b] {
                    lset $myaddr [incr i -1] $octet
                }
                set myaddr [binary format cc* 4 $myaddr]    ;# IPv6
            }
            set myport [binary format Su $myport]
            puts -nonewline $chan \5\0\0$myaddr$myport      ;# OK

            chan configure $upchan -translation binary -buffering none -blocking 0

            chan push $upchan [callback tamper_up]
            chan push $chan [callback tamper_down]

            chan copy $chan $upchan -command [list [info coroutine] client]
            chan copy $upchan $chan -command [list [info coroutine] server]
            lassign [yieldm] whom nbytes err
            log "Connection closed by $whom"
            close $chan
            close $upchan
        }
    }

    proc read {chan args} {
        if {![chan configure $chan -blocking]} {
            set was [chan event $chan readable]
            chan event $chan readable [list catch [info coroutine]]
            yield
            chan event $chan readable $was
        }
        tailcall ::read $chan {*}$args
    }

    proc log args {
        puts [list [info coroutine] {*}$args]
    }

    proc callback args {tailcall namespace code $args}

    proc yieldm args {yieldto string cat {*}$args}

    proc finally {script} {
        tailcall trace add variable :#finally#: unset [list apply [list args $script]]
    }

    namespace eval tamper_up {
        proc initialize {x mode}       { info procs }
        proc finalize {x}              { }
        proc read {x data} {
            return $data
        }
        proc write {x data} {
            if {[string match *\x00\x00\x00\x04\x31\x30\x30\x30* $data]} {
                puts "Rewriting!"
            }
            string map {
                \x00\x00\x00\x04\x31\x30\x30\x30
                \x70\x60\x50\x40\x31\x30\x30\x30
            } $data
        }
        namespace export *
        namespace ensemble create
    }

    namespace eval tamper_down {
        proc initialize {x mode}       { info procs }
        proc finalize {x}              { }
        proc read {x data} {
            return $data
        }
        proc write {x data} {
            return $data
        }
        namespace export *
        namespace ensemble create
    }
}

socks5::main {*}$::argv