GNU PEDA Pwntools Python

Введение в PEDA и Pwntools. Часть 2

Введение в PEDA и Pwntools. Часть 2

Продолжим наше знакомство с необходимым инструментарием..

Pwntools

Pwntools – это python библиотека позволяющая эффективно и быстро писать эксплойты. Очень часто её используют в CTF. Сейчас мы разберёмся как с ней работать.

Прежде всего нужно её поставить. Хотя разработчики и пишут, что лучше использовать python3, у меня на 3-ем питоне библиотека не взлетела.

Получилось поставить так:

root@kali:~# apt-get update
root@kali:~# apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential ipython
root@kali:~# pip2 install --upgrade pip
root@kali:~# pip2 install pwntools

Как использовать Pwntools

Существует три способа использования:

  1. Интерактивно через python/iPython консоль
  2. Непосредственно в скрипте python
  3. Pwntools-утилитой командной строки

Способ 1. Интерактивный через консоль

Часто при написании эксплойта есть необходимость что-то по-побыстрому проверить не создавая полноценный скрипт. Консоль iPython отличный способ изучить Pwntools API:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# ipython
Python 2.7.17 (default, Oct 19 2019, 23:36:22)
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from pwn import *

In [2]:

В iPython есть функция автодополнения и встроенная система для поиска ключевых слов в документации. К примеру, если мы хотим посмотреть что делает функция p32, просто используем символ ? :

In [4]: p32?
Signature: p32(*a, **kw)
Docstring:
p32(number, sign, endian, ...) -> str

Packs an 32-bit integer

Arguments:
    number (int): Number to convert
    endianness (str): Endianness of the converted integer ("little"/"big")
    sign (str): Signedness of the converted integer ("unsigned"/"signed")
    kwargs (dict): Arguments passed to context.local(), such as
        ``endian`` or ``signed``.

Returns:
    The packed number as a string
File:      /usr/local/lib/python2.7/dist-packages/pwnlib/context/__init__.py
Type:      function

In [5]: p32(0x41424344)
Out[5]: 'DCBA'

In [6]:

Способ 2. В скрипте python

Для начала, воспользуемся данным шаблоном:

#!/usr/bin/python

from pwn import *

def main():
    pass

if __name__ == '__main__':
    main()

Попробуем из скрипта вызвать Pwntools API:

#!/usr/bin/python

from pwn import *

def main():
    p = process("/bin/sh")
    p.interactive()

if __name__ == '__main__':
    main()

Запустим скрипт:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# python 2_shellsample.py
[+] Starting local process '/bin/sh': pid 39116
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$

Способ 3. Инструменты командной строки Pwntools

Pwntools устанавливает pwn скрипт в директорию /usr/local/bin. Для получения списка команд, просто запустите pwn -h:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# pwn -h
usage: pwn [-h]
           {asm,checksec,constgrep,cyclic,debug,disasm,disablenx,elfdiff,elfpatch,errno,hex,phd,pwnstrip,scramble,shellcraft,template,unhex,update}
           ...

Pwntools Command-line Interface

positional arguments:
  {asm,checksec,constgrep,cyclic,debug,disasm,disablenx,elfdiff,elfpatch,errno,hex,phd,pwnstrip,scramble,shellcraft,template,unhex,update}
    asm                 Assemble shellcode into bytes
    checksec            Check binary security settings
    constgrep           Looking up constants from header files. Example:
                        constgrep -c freebsd -m ^PROT_ '3 + 4'
    cyclic              Cyclic pattern creator/finder
    debug               Debug a binary in GDB
    disasm              Disassemble bytes into text format
    disablenx           Disable NX for an ELF binary
    elfdiff             Compare two ELF files
    elfpatch            Patch an ELF file
    errno               Prints out error messages
    hex                 Hex-encodes data provided on the command line or stdin
    phd                 Pwnlib HexDump
    pwnstrip            Strip binaries for CTF usage
    scramble            Shellcode encoder
    shellcraft          Microwave shellcode -- Easy, fast and delicious
    template            Generate an exploit template
    unhex               Decodes hex-encoded data provided on the command line
                        or via stdin.
    update              Check for pwntools updates

optional arguments:
  -h, --help            show this help message and exit

Полная версия документации доступна тут: https://docs.pwntools.com/en/stable/commandline.html

Взаимодействие с исполняемыми файлами

Существует множество векторов атак, давайте сосредоточимся на удалённо запущенных исполняемых файлах, к которым у нас есть доступ по сети.

Для начала, давайте посмотрим, как мы можем взаимодействовать с локальной копией файла, который на вход принимает строку из stdin и возвращает результат в stdout.

Локальная копия исполняемого файла

Попробуем запустить следующий файл:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# ./2_interactive
Welcome to the Super Secure Shell
Password: HelloWorld?
Incorrect password!

Вот исходный код запускаемого файла:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void give_shell() {
    system("/bin/sh");
}

int main() {
    // Disable buffering on stdin and stdout to make network connections better.
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    char * password = "TheRealPassword";
    char user_password[200];

    puts("Welcome to the Super Secure Shell");
    printf("Password: ");

    scanf("%199s", user_password);
    if (strcmp(password, user_password) == 0) {
        puts("Correct password!");
        give_shell();
    }
    else {
        puts("Incorrect password!");
    }
}

Главная задача программы – сравнить введёные данные и строку пароля . Если они совпадают, то запускается шелл:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# ./2_interactive
Welcome to the Super Secure Shell
Password: TheRealPassword
Correct password!
# id
uid=0(root) gid=0(root) groups=0(root)
#

Теперь мы осознали, что нужно отправить в качестве входных данных. Давайте напишем простой эксплойт используя Pwntools:

#!/usr/bin/python

from pwn import *

def main():
    # Start a local process
    p = process("../build/2_interactive")

    # Get rid of the prompt
    data1 = p.recvrepeat(0.2)
    log.info("Got data: %s" % data1)

    # Send the password
    p.sendline("TheRealPassword")

    # Check for success or failure
    data2 = p.recvline()
    log.info("Got data: %s" % data2)
    if "Correct" in data2:
        # Hand interaction over to the user if successful
        log.success("Success! Enjoy your shell!")
        p.interactive()
    else:
        log.failure("Password was incorrect.")

if __name__ == "__main__":
    main()

Потратьте немного времени для того чтобы разобраться, как работает скрипт. Обратите внимание на строку process(“../build/2_interactive”). Она запускает новый процесс и позволяет взаимодействовать с процессом как с сокетом. Давайте запустим скрипт, чтобы убедиться что всё работает:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# python 3_interactive.py
[+] Starting local process '../build/2_interactive': pid 39170
[*] Got data: Welcome to the Super Secure Shell
    Password:
[*] Got data: Correct password!
[+] Success! Enjoy your shell!
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ ls -al
total 36
drwxr-xr-x 2 root root 4096 Jan  5 17:12 .
drwxr-xr-x 6 root root 4096 Jan  5 17:12 ..
-rw-r--r-- 1 root root   98 Jan  5 17:12 1_template.py
-rw-r--r-- 1 root root  136 Jan  5 17:12 2_shellsample.py
-rw-r--r-- 1 root root  628 Jan  5 17:12 3_interactive.py
-rw-r--r-- 1 root root  663 Jan  5 17:12 4_networked.py
-rw-r--r-- 1 root root  412 Jan  5 17:12 5_gdb.py
-rw-r--r-- 1 root root  369 Jan  5 17:12 6_gdbsol.py
-rw-r--r-- 1 root root  363 Jan  5 17:12 7_gdbremote.py
$
[*] Interrupted
[*] Stopped process '../build/2_interactive' (pid 39170)

Моделирование сетевого приложения

Для начала, мы создадим новую скрин сессию, для запуска нашего сервера при помощи утилиты socat:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# screen bash
root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts#

Теперь запустим сам сервер, порт 1330:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# socat TCP4-listen:1330,reuseaddr,fork EXEC:./2_interactive

Пока тут всё. Вернёмся к нашей первой bash сессии при помощи комбинации клавиш: CTRL-A-D.

Для того, чтобы проверить что сервер работает на 1330 порту, мы можем запустить netcat:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# nc localhost 1330
Welcome to the Super Secure Shell
Password: Hello
Incorrect password!

Теперь модифицируем первый скрипт, который работал с бинарными файлами. Просто закомментируем строку запуска процесса process(), и допишем remote(“localhost”, 1330):

#!/usr/bin/python

from pwn import *

def main():
    # Start a local process
    #p = process("../build/2_interactive")
    p = remote("localhost", 1330)

    # Get rid of the prompt
    data1 = p.recvrepeat(0.2)
    log.info("Got data: %s" % data1)

    # Send the password
    p.sendline("TheRealPassword")

    # Check for success or failure
    data2 = p.recvline()
    log.info("Got data: %s" % data2)
    if "Correct" in data2:
        # Hand interaction over to the user if successful
        log.success("Success! Enjoy your shell!")
        p.interactive()
    else:
        log.failure("Password was incorrect.")

if __name__ == "__main__":
    main()

Проверяем:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# python 4_networked.py
[+] Opening connection to localhost on port 1330: Done
[*] Got data: Welcome to the Super Secure Shell
    Password:
[*] Got data: Correct password!
[+] Success! Enjoy your shell!
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ ls -al
total 36
drwxr-xr-x 2 root root 4096 Jan  5 17:12 .
drwxr-xr-x 6 root root 4096 Jan  5 17:12 ..
-rw-r--r-- 1 root root   98 Jan  5 17:12 1_template.py
-rw-r--r-- 1 root root  136 Jan  5 17:12 2_shellsample.py
-rw-r--r-- 1 root root  628 Jan  5 17:12 3_interactive.py
-rw-r--r-- 1 root root  663 Jan  5 17:12 4_networked.py
-rw-r--r-- 1 root root  412 Jan  5 17:12 5_gdb.py
-rw-r--r-- 1 root root  369 Jan  5 17:12 6_gdbsol.py
-rw-r--r-- 1 root root  363 Jan  5 17:12 7_gdbremote.py
$
[*] Closed connection to localhost port 1330

Отладка при помощи GDB

Pwntools включает в себя GDB функционал, для его изучения лучше пройтись по офф документации: http://docs.pwntools.com/en/stable/gdb.html

Давайте попробуем:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# ./3_reversing
Name: This is a Name
Token: AAAA
Submitted Name: This is a Name

Submitted Token: 0x41414141
Incorrect credentials.

Чтобы лучше понять что происходит, можно использовать утилиту ltrace:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# apt install ltrace
root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/build# ltrace ./3_reversing
__libc_start_main(0x4007b1, 1, 0x7ffcb8734588, 0x4008f0 <unfinished ...>
setvbuf(0x7f38691b7a00, 0, 2, 0)                                                                        = 0
setvbuf(0x7f38691b8760, 0, 2, 0)                                                                        = 0
printf("Name: "Name: )                                                                                        = 6
read(0This is a Name
, "This is a Name\n", 63)                                                                         = 15
printf("Token: "Token: )                                                                                       = 7
read(0AAAA
, "AAAA", 4)                                                                                      = 4
printf("Submitted Name: %s\n", "This is a Name\n"Submitted Name: This is a Name

)                                                      = 32
printf("Submitted Token: 0x%x\n", 0x41414141Submitted Token: 0x41414141
)                                                           = 28
strcmp("Santo & Johnny", "This is a Name\n")                                                            = -1
puts("Incorrect credentials."Incorrect credentials.
)                                                                          = 23
+++ exited (status 0) +++

Используя следующий скрипт, мы можем вывести id процесса перед взаимодействием с ним:

#!/usr/bin/python

from pwn import *

def main():
    # Start a new process
    p = process("../build/3_reversing")

    # Name and Token
    name = "Test Name".ljust(63, "\x00")
    token = 0x41414141

    # Print pid
    raw_input(str(p.proc.pid))

    # Send name and token
    p.send(name)
    p.send(p32(token))

    # Start an interactive session
    p.interactive()

if __name__ == "__main__":
    main()

Теперь запустим ещё одну SSH сессию и запустим наш скрипт:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# python 5_gdb.py
[+] Starting local process '../build/3_reversing': Done
5652

Вернёмся в предыдущую bash сессию и запустим GDB. Теперь мы можем присоедениться прямо к процессу:

root@kali:~/linux-exploitation-course/lessons/3_intro_to_tools/scripts# gdb
GNU gdb (Debian 8.2.1-2) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
gdb-peda$ attach 5652
Attaching to process 5652
Reading symbols from /vagrant/lessons/3_intro_to_tools/build/3_reversing...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so...done.
done.
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so...done.
done.

 [----------------------------------registers-----------------------------------]
RAX: 0xfffffffffffffe00
RBX: 0x0
RCX: 0x7f2156e17680 (<__read_nocancel+7>:	cmp    rax,0xfffffffffffff001)
RDX: 0x3f ('?')
RSI: 0x7ffc16545500 --> 0x0
RDI: 0x0
RBP: 0x7ffc16545550 --> 0x4008f0 (<__libc_csu_init>:	push   r15)
RSP: 0x7ffc165454e8 --> 0x400844 (<main+147>:	mov    edi,0x40098a)
RIP: 0x7f2156e17680 (<__read_nocancel+7>:	cmp    rax,0xfffffffffffff001)
R8 : 0x7f2157304700 (0x00007f2157304700)
R9 : 0x6
R10: 0x37b
R11: 0x246
R12: 0x400680 (<_start>:	xor    ebp,ebp)
R13: 0x7ffc16545630 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7f2156e17677 <read+7>:	jne    0x7f2156e17689 <read+25>
   0x7f2156e17679 <__read_nocancel>:	mov    eax,0x0
   0x7f2156e1767e <__read_nocancel+5>:	syscall
=> 0x7f2156e17680 <__read_nocancel+7>:	cmp    rax,0xfffffffffffff001
   0x7f2156e17686 <__read_nocancel+13>:	jae    0x7f2156e176b9 <read+73>
   0x7f2156e17688 <__read_nocancel+15>:	ret
   0x7f2156e17689 <read+25>:	sub    rsp,0x8
   0x7f2156e1768d <read+29>:	call   0x7f2156e354e0 <__libc_enable_asynccancel>
[------------------------------------stack-------------------------------------]
0000| 0x7ffc165454e8 --> 0x400844 (<main+147>:	mov    edi,0x40098a)
0008| 0x7ffc165454f0 --> 0x0
0016| 0x7ffc165454f8 --> 0x4141414100000000 ('')
0024| 0x7ffc16545500 --> 0x0
0032| 0x7ffc16545508 --> 0x0
0040| 0x7ffc16545510 --> 0x0
0048| 0x7ffc16545518 --> 0x0
0056| 0x7ffc16545520 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00007f2156e17680 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: No such file or directory.
gdb-peda$

Программа остановлена где то в библиотеке libc. Мы можем установить наши точки останова. Давайте предположим, что мы уже методом реверса определили куда передаются входные данные – в функцию check_creds():

gdb-peda$ disas check_creds
Dump of assembler code for function check_creds:
   0x0000000000400776 <+0>:	push   rbp
   0x0000000000400777 <+1>:	mov    rbp,rsp
   0x000000000040077a <+4>:	sub    rsp,0x10
   0x000000000040077e <+8>:	mov    QWORD PTR [rbp-0x8],rdi
   0x0000000000400782 <+12>:	mov    DWORD PTR [rbp-0xc],esi
   0x0000000000400785 <+15>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400789 <+19>:	mov    rsi,rax
   0x000000000040078c <+22>:	mov    edi,0x400974
   0x0000000000400791 <+27>:	call   0x400650 <strcmp@plt>
   0x0000000000400796 <+32>:	test   eax,eax
   0x0000000000400798 <+34>:	je     0x4007aa <check_creds+52>
   0x000000000040079a <+36>:	cmp    DWORD PTR [rbp-0xc],0xdeadc0de
   0x00000000004007a1 <+43>:	jne    0x4007aa <check_creds+52>
   0x00000000004007a3 <+45>:	mov    eax,0x0
   0x00000000004007a8 <+50>:	jmp    0x4007af <check_creds+57>
   0x00000000004007aa <+52>:	mov    eax,0x1
   0x00000000004007af <+57>:	leave
   0x00000000004007b0 <+58>:	ret
End of assembler dump.
gdb-peda$

Давайте установим несколько точек останова на функции и пошагово продолжим выполнение. Таким образом мы сможем определить, какие данные нужно подать на вход, чтобы пройти проверку:

gdb-peda$ br check_creds
Breakpoint 1 at 0x40077a
gdb-peda$ c
Continuing.
gdb-peda$

Вернитесь к терминалу где запущен скрипт, нажмите Enter для отправки данных. В терминале где открыт GDB, отладчик должен прервать выполнение в точке останова. Продолжайте отладку чтобы получить нужные значения.

Очень злой админ
Очень злой админ Автор статьи

Админ сайта. Публикует интересные статьи с других ресурсов, либо их переводы. Если есть настроение, бывает, что пишет и что-то своё.

Leave a Reply

Your email address will not be published. Required fields are marked *