pwntools 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -3
  3. data/Rakefile +3 -1
  4. data/lib/pwnlib/asm.rb +172 -2
  5. data/lib/pwnlib/constants/constants.rb +10 -3
  6. data/lib/pwnlib/context.rb +1 -3
  7. data/lib/pwnlib/elf/elf.rb +3 -3
  8. data/lib/pwnlib/errors.rb +30 -0
  9. data/lib/pwnlib/ext/helper.rb +1 -1
  10. data/lib/pwnlib/logger.rb +140 -2
  11. data/lib/pwnlib/pwn.rb +3 -0
  12. data/lib/pwnlib/reg_sort.rb +1 -1
  13. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +9 -3
  14. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +6 -2
  15. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +6 -2
  16. data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +23 -0
  17. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +6 -4
  18. data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +23 -0
  19. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +6 -2
  20. data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +23 -0
  21. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +6 -2
  22. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +6 -4
  23. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +9 -3
  24. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +6 -2
  25. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +6 -2
  26. data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +23 -0
  27. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +8 -4
  28. data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +23 -0
  29. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +6 -2
  30. data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +23 -0
  31. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +6 -2
  32. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +8 -4
  33. data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +53 -0
  34. data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +33 -0
  35. data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +46 -0
  36. data/lib/pwnlib/shellcraft/shellcraft.rb +3 -2
  37. data/lib/pwnlib/timer.rb +5 -2
  38. data/lib/pwnlib/tubes/process.rb +153 -0
  39. data/lib/pwnlib/tubes/serialtube.rb +112 -0
  40. data/lib/pwnlib/tubes/sock.rb +24 -25
  41. data/lib/pwnlib/tubes/tube.rb +191 -39
  42. data/lib/pwnlib/util/packing.rb +3 -9
  43. data/lib/pwnlib/version.rb +1 -1
  44. data/test/asm_test.rb +85 -2
  45. data/test/constants/constants_test.rb +2 -2
  46. data/test/data/echo.rb +2 -7
  47. data/test/elf/elf_test.rb +10 -15
  48. data/test/files/use_pwn.rb +2 -6
  49. data/test/logger_test.rb +38 -0
  50. data/test/shellcraft/linux/cat_test.rb +86 -0
  51. data/test/shellcraft/linux/syscalls/exit_test.rb +56 -0
  52. data/test/shellcraft/linux/syscalls/open_test.rb +86 -0
  53. data/test/shellcraft/shellcraft_test.rb +5 -4
  54. data/test/test_helper.rb +22 -2
  55. data/test/timer_test.rb +19 -1
  56. data/test/tubes/process_test.rb +99 -0
  57. data/test/tubes/serialtube_test.rb +165 -0
  58. data/test/tubes/sock_test.rb +20 -21
  59. data/test/tubes/tube_test.rb +86 -16
  60. metadata +75 -13
@@ -71,9 +71,7 @@ module Pwnlib
71
71
  end
72
72
  else
73
73
  if is_all
74
- if number < 0
75
- raise ArgumentError, "Can't pack negative number with bits='all' and signed=false"
76
- end
74
+ raise ArgumentError, "Can't pack negative number with bits='all' and signed=false" if number < 0
77
75
  bits = number.zero? ? 8 : ((number.bit_length - 1) | 7) + 1
78
76
  end
79
77
 
@@ -136,9 +134,7 @@ module Pwnlib
136
134
  signed = context.signed
137
135
  bytes = (bits + 7) / 8
138
136
 
139
- unless data.size == bytes
140
- raise ArgumentError, "data.size=#{data.size} does not match with bits=#{bits}"
141
- end
137
+ raise ArgumentError, "data.size=#{data.size} does not match with bits=#{bits}" unless data.size == bytes
142
138
 
143
139
  data = data.reverse if endian == 'little'
144
140
  data = data.unpack('C*')
@@ -200,9 +196,7 @@ module Pwnlib
200
196
 
201
197
  bytes = bits / 8
202
198
 
203
- if data.size % bytes != 0
204
- raise ArgumentError, "data.size=#{data.size} must be a multiple of bytes=#{bytes}"
205
- end
199
+ raise ArgumentError, "data.size=#{data.size} must be a multiple of bytes=#{bytes}" if data.size % bytes != 0
206
200
  ret = []
207
201
  (data.size / bytes).times do |idx|
208
202
  x1 = idx * bytes
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Pwnlib
4
4
  # version of pwntools-ruby
5
- VERSION = '1.0.1'.freeze
5
+ VERSION = '1.1.0'.freeze
6
6
  end
@@ -10,11 +10,19 @@ class AsmTest < MiniTest::Test
10
10
  Asm = ::Pwnlib::Asm
11
11
 
12
12
  def setup
13
- skip 'Not test asm/disasm on Windows' if TTY::Platform.new.windows?
14
13
  @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
15
14
  end
16
15
 
16
+ def skip_windows
17
+ skip 'Not test asm/disasm on Windows' if TTY::Platform.new.windows?
18
+ end
19
+
20
+ def linux_only
21
+ skip 'ELF can only be executed on Linux' unless TTY::Platform.new.linux?
22
+ end
23
+
17
24
  def test_i386_asm
25
+ skip_windows
18
26
  context.local(arch: 'i386') do
19
27
  assert_equal("\x90", Asm.asm('nop'))
20
28
  assert_equal("\xeb\xfe", Asm.asm(@shellcraft.infloop))
@@ -26,6 +34,7 @@ class AsmTest < MiniTest::Test
26
34
  end
27
35
 
28
36
  def test_amd64_asm
37
+ skip_windows
29
38
  context.local(arch: 'amd64') do
30
39
  assert_equal("\x90", Asm.asm('nop'))
31
40
  assert_equal("\xeb\xfe", Asm.asm(@shellcraft.infloop))
@@ -36,6 +45,7 @@ class AsmTest < MiniTest::Test
36
45
  end
37
46
 
38
47
  def test_i386_disasm
48
+ skip_windows
39
49
  context.local(arch: 'i386') do
40
50
  str = Asm.disasm("h\x01\x01\x01\x01\x814$ri\x01\x011\xd2"\
41
51
  "Rj\x04Z\x01\xe2R\x89\xe2jhh///sh/binj\x0bX\x89\xe3\x89\xd1\x99\xcd\x80")
@@ -66,6 +76,7 @@ class AsmTest < MiniTest::Test
66
76
  end
67
77
 
68
78
  def test_amd64_disasm
79
+ skip_windows
69
80
  context.local(arch: 'amd64') do
70
81
  str = Asm.disasm("hri\x01\x01\x814$\x01\x01\x01\x011\xd2" \
71
82
  "Rj\x08ZH\x01\xe2RH\x89\xe2jhH\xb8/bin///sPj;XH\x89\xe7H\x89\xd6\x99\x0f\x05", vma: 0xfff)
@@ -98,7 +109,79 @@ class AsmTest < MiniTest::Test
98
109
 
99
110
  # To ensure coverage
100
111
  def test_require
101
- err = assert_raises(LoadError) { Asm.__send__(:require_message, 'no_such_lib', 'meow') }
112
+ err = assert_raises(::Pwnlib::Errors::DependencyError) do
113
+ Asm.__send__(:require_message, 'no_such_lib', 'meow')
114
+ end
102
115
  assert_match(/meow/, err.message)
103
116
  end
117
+
118
+ def make_elf_file(*args)
119
+ elf = Asm.make_elf(*args)
120
+ stream = StringIO.new(elf)
121
+ [elf, ::ELFTools::ELFFile.new(stream)]
122
+ end
123
+
124
+ def test_make_elf
125
+ # create ELF and use ELFTools to test it
126
+ data = 'not important'
127
+ elf, elf_file = make_elf_file(data)
128
+ assert_equal('Intel 80386', elf_file.machine)
129
+ # check entry point contains data
130
+ assert_equal(data, elf[elf_file.header.e_entry - 0x08048000, data.size])
131
+ segment = elf_file.segments.first
132
+ assert(segment.readable? && segment.executable?)
133
+
134
+ # test to_file
135
+ temp_path = Asm.make_elf(data, to_file: true)
136
+ assert_equal(elf, IO.binread(temp_path))
137
+ File.unlink(temp_path)
138
+
139
+ # test block form
140
+ temp_path = Asm.make_elf(data) do |path|
141
+ assert(File.file?(path))
142
+ path
143
+ end
144
+ # check file removed
145
+ refute(File.exist?(temp_path))
146
+
147
+ # test vma
148
+ custom_base = 0x1234000
149
+ _, elf_file = make_elf_file(data, vma: custom_base)
150
+ assert_equal(custom_base, elf_file.segments.first.header.p_vaddr)
151
+
152
+ context.local(arch: :arm) do
153
+ _, elf_file = make_elf_file(data)
154
+ assert_equal(32, elf_file.header.elf_class)
155
+ assert_equal('ARM', elf_file.machine)
156
+ assert_equal(0x8000, elf_file.segments.first.header.p_vaddr)
157
+ end
158
+
159
+ context.local(arch: :vax) do
160
+ err = assert_raises(::Pwnlib::Errors::UnsupportedArchError) do
161
+ Asm.make_elf('')
162
+ end
163
+ assert_equal('Unknown machine type of architecture "vax".', err.message)
164
+ end
165
+ end
166
+
167
+ # this test can be removed after method +run_shellcode+ being implemented
168
+ def test_make_elf_and_run
169
+ # run the ELF we created to make sure it works.
170
+ linux_only
171
+
172
+ # test supported architecture
173
+ {
174
+ i386: /08048000-08049000 r-xp/,
175
+ amd64: /00400000-00401000 r-xp/
176
+ }.each do |arch, regexp|
177
+ context.local(arch: arch) do
178
+ data = Asm.asm(@shellcraft.cat('/proc/self/maps') + @shellcraft.syscall('SYS_exit', 0))
179
+ Asm.make_elf(data) do |path|
180
+ Open3.popen2(path) do |_i, o, _t|
181
+ assert_match(regexp, o.gets)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
104
187
  end
@@ -15,8 +15,8 @@ class ConstantsTest < MiniTest::Test
15
15
  assert_equal('__NR_arch_prctl', Constants.__NR_arch_prctl.to_s)
16
16
  assert_equal('Constant("(O_CREAT)", 0x40)', Constants.eval('O_CREAT').inspect)
17
17
  assert_equal('Constant("(O_CREAT | O_WRONLY)", 0x41)', Constants.eval('O_CREAT | O_WRONLY').inspect)
18
- err = assert_raises(NameError) { Constants.eval('rax') }
19
- assert_equal('no value provided for variables: rax', err.message)
18
+ err = assert_raises(::Pwnlib::Errors::ConstantNotFoundError) { Constants.eval('rax + rbx') }
19
+ assert_equal('Undefined constant(s): rax, rbx', err.message)
20
20
  end
21
21
  end
22
22
 
@@ -2,14 +2,9 @@
2
2
 
3
3
  require 'socket'
4
4
 
5
- if ARGV.empty?
6
- puts "Usage #{__FILE__} port"
7
- exit 1
8
- end
5
+ server = TCPServer.open('127.0.0.1', 0)
9
6
 
10
- server = TCPServer.open('127.0.0.1', ARGV[0].to_i)
11
-
12
- STDOUT.puts 'Start!'
7
+ STDOUT.puts "Start with port #{server.addr[1]}"
13
8
  STDOUT.flush
14
9
 
15
10
  client = server.accept
@@ -6,11 +6,13 @@ require 'pwnlib/elf/elf'
6
6
  require 'pwnlib/logger'
7
7
 
8
8
  class ELFTest < MiniTest::Test
9
- include ::Pwnlib::Logger
10
-
11
9
  def setup
12
10
  @path_of = ->(file) { File.join(__dir__, '..', 'data', 'elfs', file) }
13
- @elf = ::Pwnlib::ELF::ELF.new(@path_of.call('i386.prelro.elf'), checksec: false)
11
+ @elf = to_elf_silent('i386.prelro.elf')
12
+ end
13
+
14
+ def to_elf_silent(filename)
15
+ log_null { ::Pwnlib::ELF::ELF.new(@path_of.call(filename), checksec: false) }
14
16
  end
15
17
 
16
18
  # check stdout when loaded
@@ -43,7 +45,7 @@ NX: NX enabled
43
45
  PIE: No PIE (0x8048000)
44
46
  EOS
45
47
 
46
- nrelro_elf = ::Pwnlib::ELF::ELF.new(@path_of.call('amd64.nrelro.elf'), checksec: false)
48
+ nrelro_elf = to_elf_silent('amd64.nrelro.elf')
47
49
  assert_equal(<<-EOS.strip, nrelro_elf.checksec)
48
50
  RELRO: No RELRO
49
51
  Stack: Canary found
@@ -51,7 +53,7 @@ NX: NX enabled
51
53
  PIE: No PIE (0x400000)
52
54
  EOS
53
55
 
54
- frelro_elf = ::Pwnlib::ELF::ELF.new(@path_of.call('amd64.frelro.elf'), checksec: false)
56
+ frelro_elf = to_elf_silent('amd64.frelro.elf')
55
57
  assert_equal(<<-EOS.strip, frelro_elf.checksec)
56
58
  RELRO: Full RELRO
57
59
  Stack: Canary found
@@ -78,7 +80,7 @@ PIE: No PIE (0x400000)
78
80
  assert_same(0x80483b0, @elf.plt.printf)
79
81
  assert_same(0x80483f0, @elf.plt[:scanf])
80
82
 
81
- elf = ::Pwnlib::ELF::ELF.new(@path_of.call('amd64.frelro.pie.elf'), checksec: false)
83
+ elf = to_elf_silent('amd64.frelro.pie.elf')
82
84
  assert_nil(elf.plt)
83
85
  end
84
86
 
@@ -90,7 +92,7 @@ PIE: No PIE (0x400000)
90
92
  @elf.address = new_address
91
93
  assert_equal(old_main - old_address + new_address, @elf.symbols.main)
92
94
 
93
- elf = ::Pwnlib::ELF::ELF.new(@path_of.call('i386.frelro.pie.elf'), checksec: false)
95
+ elf = to_elf_silent('i386.frelro.pie.elf')
94
96
  assert_equal(0, elf.address)
95
97
  assert_same(0x6c2, elf.symbols.main)
96
98
  elf.address = 0xdeadbeef0000
@@ -99,7 +101,7 @@ PIE: No PIE (0x400000)
99
101
  end
100
102
 
101
103
  def test_static
102
- elf = ::Pwnlib::ELF::ELF.new(@path_of.call('amd64.static.elf'), checksec: false)
104
+ elf = to_elf_silent('amd64.static.elf')
103
105
  assert_equal(<<-EOS.strip, elf.checksec)
104
106
  RELRO: Partial RELRO
105
107
  Stack: Canary found
@@ -124,11 +126,4 @@ PIE: No PIE (0x400000)
124
126
  assert_equal([0x1234001, 0x1392613], elf.search('ELF').to_a)
125
127
  assert_equal(0x138d00b, elf.find('/bin/sh').next)
126
128
  end
127
-
128
- def log_stdout
129
- old = log.instance_variable_get(:@logdev)
130
- log.instance_variable_set(:@logdev, $stdout)
131
- yield
132
- log.instance_variable_set(:@logdev, old)
133
- end
134
129
  end
@@ -8,12 +8,8 @@ require 'pwn'
8
8
  context[arch: 'amd64']
9
9
 
10
10
  raise 'pack fail' unless pack(1) == "\x01\0\0\0\0\0\0\0"
11
- unless ::Pwnlib::Util::Fiddling.__send__(:context).object_id == context.object_id
12
- raise 'not unique context'
13
- end
14
- unless ::Pwnlib::Context.context.object_id == context.object_id
15
- raise 'not unique context'
16
- end
11
+ raise 'not unique context' unless ::Pwnlib::Util::Fiddling.__send__(:context).object_id == context.object_id
12
+ raise 'not unique context' unless ::Pwnlib::Context.context.object_id == context.object_id
17
13
 
18
14
  # Make sure things aren't polluting Object
19
15
  begin
@@ -58,4 +58,42 @@ class LoggerTest < MiniTest::Test
58
58
  meowmeowmeow
59
59
  EOS
60
60
  end
61
+
62
+ def test_dump
63
+ x = 2
64
+ y = 3
65
+ assert_equal(<<-EOS, @logger.dump(x + y, x * y))
66
+ [DUMP] (x + y) = 5, (x * y) = 6
67
+ EOS
68
+
69
+ libc = 0x7fc0bdd13000
70
+ # check if source code parsing works good
71
+ msg = @logger.dump(
72
+ libc # comment is ok
73
+ .to_s(16),
74
+ libc - libc
75
+ )
76
+ assert_equal(<<-EOS, msg)
77
+ [DUMP] libc.to_s(16) = "7fc0bdd13000", (libc - libc) = 0
78
+ EOS
79
+
80
+ libc = 0x7fc0bdd13000
81
+ assert_equal(<<-EOS, @logger.dump { libc.to_s(16) })
82
+ [DUMP] libc.to_s(16) = "7fc0bdd13000"
83
+ EOS
84
+
85
+ res = @logger.dump do
86
+ libc = 12_345_678
87
+ libc <<= 12
88
+ # comments will be ignored
89
+ libc.to_s # dummy line
90
+ libc.to_s(16)
91
+ end
92
+ assert_equal(<<-EOS, res)
93
+ [DUMP] libc = 12345678
94
+ libc = (libc << 12)
95
+ libc.to_s
96
+ libc.to_s(16) = "bc614e000"
97
+ EOS
98
+ end
61
99
  end
@@ -0,0 +1,86 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/context'
6
+ require 'pwnlib/shellcraft/shellcraft'
7
+
8
+ class CatTest < MiniTest::Test
9
+ include ::Pwnlib::Context
10
+
11
+ def setup
12
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
13
+ end
14
+
15
+ def test_amd64
16
+ context.local(arch: 'amd64') do
17
+ assert_equal(<<-'EOS', @shellcraft.cat('flag'))
18
+ /* push "flag\x00" */
19
+ push 0x67616c66
20
+ /* call open("rsp", "O_RDONLY", 0) */
21
+ push 2 /* (SYS_open) */
22
+ pop rax
23
+ mov rdi, rsp
24
+ xor esi, esi /* (O_RDONLY) */
25
+ cdq /* rdx=0 */
26
+ syscall
27
+ /* call sendfile(1, "rax", 0, 2147483647) */
28
+ push 1
29
+ pop rdi
30
+ mov rsi, rax
31
+ push 0x28 /* (SYS_sendfile) */
32
+ pop rax
33
+ mov r10d, 0x7fffffff
34
+ cdq /* rdx=0 */
35
+ syscall
36
+ EOS
37
+ assert_equal(<<-'EOS', @shellcraft.cat('flag', fd: 2))
38
+ /* push "flag\x00" */
39
+ push 0x67616c66
40
+ /* call open("rsp", "O_RDONLY", 0) */
41
+ push 2 /* (SYS_open) */
42
+ pop rax
43
+ mov rdi, rsp
44
+ xor esi, esi /* (O_RDONLY) */
45
+ cdq /* rdx=0 */
46
+ syscall
47
+ /* call sendfile(2, "rax", 0, 2147483647) */
48
+ push 2
49
+ pop rdi
50
+ mov rsi, rax
51
+ push 0x28 /* (SYS_sendfile) */
52
+ pop rax
53
+ mov r10d, 0x7fffffff
54
+ cdq /* rdx=0 */
55
+ syscall
56
+ EOS
57
+ end
58
+ end
59
+
60
+ def test_i386
61
+ context.local(arch: 'i386') do
62
+ assert_equal(<<-'EOS', @shellcraft.cat('flag'))
63
+ /* push "flag\x00" */
64
+ push 1
65
+ dec byte ptr [esp]
66
+ push 0x67616c66
67
+ /* call open("esp", "O_RDONLY", 0) */
68
+ push 5 /* (SYS_open) */
69
+ pop eax
70
+ mov ebx, esp
71
+ xor ecx, ecx /* (O_RDONLY) */
72
+ cdq /* edx=0 */
73
+ int 0x80
74
+ /* call sendfile(1, "eax", 0, 2147483647) */
75
+ push 1
76
+ pop ebx
77
+ mov ecx, eax
78
+ xor eax, eax
79
+ mov al, 0xbb /* (SYS_sendfile) */
80
+ mov esi, 0x7fffffff
81
+ cdq /* edx=0 */
82
+ int 0x80
83
+ EOS
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+
5
+ require 'pwnlib/context'
6
+ require 'pwnlib/shellcraft/shellcraft'
7
+
8
+ class ExitTest < MiniTest::Test
9
+ include ::Pwnlib::Context
10
+
11
+ def setup
12
+ @shellcraft = ::Pwnlib::Shellcraft::Shellcraft.instance
13
+ end
14
+
15
+ def test_amd64
16
+ context.local(arch: 'amd64') do
17
+ assert_equal(<<-'EOS', @shellcraft.exit)
18
+ /* call exit(0) */
19
+ push 0x3c /* (SYS_exit) */
20
+ pop rax
21
+ xor edi, edi /* 0 */
22
+ syscall
23
+ EOS
24
+
25
+ assert_equal(<<-'EOS', @shellcraft.exit(1))
26
+ /* call exit(1) */
27
+ push 0x3c /* (SYS_exit) */
28
+ pop rax
29
+ push 1
30
+ pop rdi
31
+ syscall
32
+ EOS
33
+ end
34
+ end
35
+
36
+ def test_i386
37
+ context.local(arch: 'i386') do
38
+ assert_equal(<<-'EOS', @shellcraft.exit)
39
+ /* call exit(0) */
40
+ push 1 /* (SYS_exit) */
41
+ pop eax
42
+ xor ebx, ebx /* 0 */
43
+ int 0x80
44
+ EOS
45
+
46
+ assert_equal(<<-'EOS', @shellcraft.exit(1))
47
+ /* call exit(1) */
48
+ push 1 /* (SYS_exit) */
49
+ pop eax
50
+ push 1
51
+ pop ebx
52
+ int 0x80
53
+ EOS
54
+ end
55
+ end
56
+ end