pwn 0.5.718 → 0.5.719

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: 3c9afec4ab615d169982f573cf42ccd571c2593eec77e5905c131adf0bfb7660
4
- data.tar.gz: 12d38fd2f938a1d6a68dc0418da107207297d23e0bd4d6a4561e9080f943875b
3
+ metadata.gz: 898622177b06081e32121144c9ae5461d510482e70a85b62de425967933e4e24
4
+ data.tar.gz: ddfe5cac2e3db9bab8ab9cafdb1f48a2e2191545a2d0ad5bb7245cb7f45ab45f
5
5
  SHA512:
6
- metadata.gz: bfceaf016937af5fe50f5a47b3e4b23bda9c9dc3395c602bd17d36bea8dc9e18eec1b0372d11aea34fe5f856ce323b02368b31094ca28817b5a5beedacae89e7
7
- data.tar.gz: 3896ef7de35eae9e17c043ea36b1f9258db192f776ee977aa37dde2f5470cd1441109caea605a631bea5ca7a5446aa3a231db779dd83423a003793989c55eca9
6
+ metadata.gz: 2751aa2984fe13fe852a6f12577bd840f097e5d84f240a05642e374938bbf8fe9095dcaee15a607b0b1c3c4b90d65b787f041ffbda26ec3868f26f68a400b28b
7
+ data.tar.gz: db70fe6f75eaa22625849d30ab0e1a73dec982e160db005cce3096e625346eaea5d68653a4fb27a6e452f4ba01c63ab0313a5bd484b54a874f95bf44944ea149
@@ -661,7 +661,6 @@ module PWN
661
661
 
662
662
  private_class_method def self.declared_contract_unsatisfied?(opts = {})
663
663
  return false if verify_passed?(messages: opts[:messages])
664
- return false if write_or_read_evidenced?(messages: opts[:messages])
665
664
 
666
665
  completion_unmet(opts).any?
667
666
  rescue StandardError
@@ -670,7 +669,6 @@ module PWN
670
669
 
671
670
  private_class_method def self.completion_unmet(opts = {})
672
671
  return [] if verify_passed?(messages: opts[:messages])
673
- return [] if write_or_read_evidenced?(messages: opts[:messages])
674
672
 
675
673
  contract = declared_contract(request: opts[:request])
676
674
  request = opts[:request].to_s
@@ -182,8 +182,7 @@ module PWN
182
182
  end
183
183
  found << name if ok
184
184
  end
185
- owned = pwn_installed_bins(pwn_bins: opts[:pwn_bins])
186
- native = (found.map(&:to_s) - owned).reject(&:empty?).uniq.sort
185
+ native = found.map(&:to_s).reject(&:empty?).uniq.sort
187
186
  cap = if opts.key?(:cap_net_raw)
188
187
  opts[:cap_net_raw]
189
188
  else
@@ -207,27 +207,51 @@ module PWN
207
207
  end
208
208
 
209
209
  private_class_method def self.gtfobins_json(opts = {})
210
- body = opts[:body].to_s
211
- stripped = body.lstrip
212
- return body if stripped.start_with?('{', '[')
210
+ raw = opts[:body]
211
+ raw = raw.to_s.b unless raw.nil?
212
+ stripped = raw.to_s.lstrip
213
+ if stripped.start_with?('{', '[')
214
+ parsed = JSON.parse(stripped)
215
+ raise 'ERROR: GTFOBins JSON was empty' if parsed.nil? || parsed.empty?
216
+
217
+ return stripped
218
+ end
213
219
 
214
220
  bins = {}
215
- Zlib::GzipReader.wrap(StringIO.new(body)) do |gz|
221
+ Zlib::GzipReader.wrap(StringIO.new(raw)) do |gz|
216
222
  Gem::Package::TarReader.new(gz) do |tar|
217
223
  tar.each do |entry|
218
224
  next unless entry.file?
219
- next unless entry.full_name.include?('_gtfobins/') && entry.full_name.end_with?('.md')
220
225
 
221
- name = File.basename(entry.full_name, '.md')
226
+ path = entry.full_name.to_s
227
+ next unless path.include?('/_gtfobins/') || path.start_with?('_gtfobins/')
228
+
229
+ base = File.basename(path)
230
+ next if base.empty? || base.start_with?('.')
231
+
232
+ name = base.sub(/\.md\z/i, '')
222
233
  text = entry.read.to_s
223
- funcs = text.scan(/^##\s+(.+)$/).flatten.map(&:strip)
234
+ funcs = gtfobins_functions(text: text)
224
235
  bins[name] = { 'binary' => name, 'functions' => funcs }
225
236
  end
226
237
  end
227
238
  end
239
+ raise 'ERROR: GTFOBins archive contained no _gtfobins entries' if bins.empty?
240
+
228
241
  JSON.generate(bins)
229
- rescue StandardError
230
- body.start_with?('{') ? body : JSON.generate({})
242
+ end
243
+
244
+ private_class_method def self.gtfobins_functions(opts = {})
245
+ text = opts[:text].to_s
246
+ headed = text.scan(/^##\s+(.+)$/).flatten.map(&:strip)
247
+ return headed unless headed.empty?
248
+
249
+ if text =~ /^functions:\s*$/
250
+ keys = text.scan(/^(?: |\t)([A-Za-z0-9._-]+):\s*$/).flatten
251
+ return keys unless keys.empty?
252
+ end
253
+
254
+ []
231
255
  end
232
256
  end
233
257
  end
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.718'
4
+ VERSION = '0.5.719'
5
5
  end
@@ -931,7 +931,7 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
931
931
  {
932
932
  role: 'tool',
933
933
  name: 'pwn_eval',
934
- content: '{"success":true,"result":{"stdout":"Report saved to /tmp/pentest_report.json"},"effect":"write"}'
934
+ content: '{"success":true,"result":{"stdout":"Report saved to /tmp/pwn-spec-report.json"},"effect":"write"}'
935
935
  },
936
936
  {
937
937
  role: 'assistant',
@@ -76,8 +76,6 @@ describe PWN::Plugins::DetectOS do
76
76
  expect(row[:version]).to eq('2026.3')
77
77
  expect(row[:arch]).to be_a(String)
78
78
  expect(row[:endian]).to be_a(Symbol)
79
- expect(row[:native]).to include('jq', 'yara')
80
- expect(row[:native]).not_to include('gdb', 'r2', 'nuclei')
81
79
  expect(row).not_to have_key(:missing)
82
80
  expect(row[:summary]).to include('kali')
83
81
  expect(row[:summary]).not_to include('missing=')
@@ -94,17 +92,5 @@ describe PWN::Plugins::DetectOS do
94
92
  expect(row[:version]).to eq('3.20.0')
95
93
  expect(row[:native]).to eq([])
96
94
  end
97
-
98
- it 'does not treat plugin required_bins gaps as the inventory' do
99
- row = described_class.living_off_the_land(
100
- os_release: "ID=debian\nVERSION_ID=12\n",
101
- type: :linux,
102
- pwn_bins: %w[gdb r2],
103
- bins: %w[jq],
104
- which: { 'jq' => true, 'gdb' => false }
105
- )
106
- expect(row[:native]).to include('jq')
107
- expect(row[:native]).not_to include('gdb')
108
- end
109
95
  end
110
96
  end
@@ -3,6 +3,9 @@
3
3
  require 'spec_helper'
4
4
  require 'tmpdir'
5
5
  require 'fileutils'
6
+ require 'stringio'
7
+ require 'zlib'
8
+ require 'rubygems/package'
6
9
 
7
10
  describe PWN::Plugins::ExploitDB do
8
11
  it 'should display information for authors' do
@@ -36,23 +39,55 @@ describe PWN::Plugins::ExploitDB do
36
39
  end
37
40
  end
38
41
 
42
+ def gtfobins_tarball(opts = {})
43
+ name = (opts[:name] || 'tar').to_s
44
+ md = (opts[:body] || "---\nfunctions:\n file-read:\n - code: tar xf -\n SUID:\n - code: tar\n").to_s
45
+ sio = StringIO.new
46
+ sio.binmode
47
+ gz = Zlib::GzipWriter.new(sio)
48
+ Gem::Package::TarWriter.new(gz) do |tar|
49
+ path = "GTFOBins.github.io-master/_gtfobins/#{name}"
50
+ tar.add_file_simple(path, 0o644, md.bytesize) { |io| io.write(md) }
51
+ end
52
+ gz.close
53
+ sio.string
54
+ end
55
+
39
56
  it 'sync fetches latest ExploitDB CSV and GTFOBins into ~/.pwn/intel' do
40
57
  Dir.mktmpdir do |dir|
41
58
  allow(Dir).to receive(:home).and_return(dir)
42
59
  csv = "id,file,description,cve\n1,e.rb,Apache Log4j RCE,CVE-2021-44228\n"
43
- json = '{"tar":{"binary":"tar","functions":["file-read"]}}'
60
+ tgz = gtfobins_tarball
44
61
  get = lambda do |url|
45
- url.to_s.include?('files_exploits') ? csv : json
62
+ url.to_s.include?('files_exploits') ? csv : tgz
46
63
  end
47
64
  out = described_class.sync(get: get)
48
65
  intel = File.join(dir, '.pwn', 'intel')
49
66
  expect(out[:dest]).to eq(intel)
50
67
  expect(File.read(File.join(intel, 'files_exploits.csv'))).to include('CVE-2021-44228')
51
- expect(File.read(File.join(intel, 'gtfobins.json'))).to include('tar')
68
+ json = File.read(File.join(intel, 'gtfobins.json'))
69
+ expect(json).not_to eq('{}')
70
+ expect(json).to include('tar')
71
+ expect(json).to include('file-read')
52
72
  expect(out[:fetched]).to include('files_exploits.csv', 'gtfobins.json')
53
73
  end
54
74
  end
55
75
 
76
+ it 'refuses to write an empty gtfobins.json when the archive has no bins' do
77
+ Dir.mktmpdir do |dir|
78
+ allow(Dir).to receive(:home).and_return(dir)
79
+ sio = StringIO.new
80
+ sio.binmode
81
+ gz = Zlib::GzipWriter.new(sio)
82
+ Gem::Package::TarWriter.new(gz) { |tar| tar.mkdir('empty', 0o755) }
83
+ gz.close
84
+ get = lambda do |url|
85
+ url.to_s.include?('files_exploits') ? "id,file\n1,a\n" : sio.string
86
+ end
87
+ expect { described_class.sync(get: get) }.to raise_error(/GTFOBins/)
88
+ end
89
+ end
90
+
56
91
  it 'does not copy from a tree-bundled etc/intel directory' do
57
92
  src = File.read(described_class.method(:sync).source_location.first)
58
93
  expect(src).not_to include('etc/intel')
@@ -2002,6 +2002,7 @@
2002
2002
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.authors`: "}]}
2003
2003
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.cve_for_cpe Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.cve_for_cpe`: "}]}
2004
2004
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.gtfobins Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.gtfobins`: "}]}
2005
+ {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.gtfobins_functions Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.gtfobins_functions`: "}]}
2005
2006
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.gtfobins_json Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.gtfobins_json`: "}]}
2006
2007
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.help Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.help`: "}]}
2007
2008
  {"messages":[{"role":"user","content":"PWN::Plugins::ExploitDB.http_get Usage"},{"role":"assistant","content":"`PWN::Plugins::ExploitDB.http_get`: "}]}
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.718
4
+ version: 0.5.719
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.