Skip to content

Commit 6ddc6d8

Browse files
committed
Add lab report for security of IT systems and services (Crackme - modification of binary files)
1 parent f1570d4 commit 6ddc6d8

File tree

2 files changed

+266
-0
lines changed

2 files changed

+266
-0
lines changed

BSiUI2/Lab6/main.tex

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
\documentclass[12pt,a4paper,titlepage]{article}
2+
\usepackage[utf8]{inputenc}
3+
\usepackage{polski}
4+
\usepackage{listings}
5+
\usepackage{graphicx}
6+
\usepackage{xcolor}
7+
\usepackage{floatrow}
8+
\usepackage{minted}
9+
\usepackage{amsmath}
10+
\usepackage{caption}
11+
\usepackage{hyperref}
12+
13+
\renewcommand\listoflistingscaption{Spis listingów}
14+
15+
\setminted{
16+
autogobble,
17+
breaklines,
18+
framerule=1pt,
19+
framesep=10pt
20+
}
21+
22+
\newenvironment{longlisting}{}{}
23+
24+
\makeatletter
25+
\newcommand{\linia}{\rule{\linewidth}{0.4mm}}
26+
\renewcommand{\maketitle}{\begin{titlepage}
27+
\vspace*{1cm}
28+
\begin{center}\small
29+
Politechnika Wrocławska\\
30+
Wydział Elektroniki\\
31+
Bezpieczeństwo Systemów i Usług Informatycznych 2
32+
\end{center}
33+
\vspace{3cm}
34+
\noindent\linia
35+
\begin{center}
36+
\LARGE \textsc{\@title}
37+
\end{center}
38+
\linia
39+
\vspace{0.5cm}
40+
\begin{flushright}
41+
\begin{minipage}{7cm}
42+
\textit{\small Autor:}\\
43+
\normalsize \textsc{\@author} \par
44+
\end{minipage}
45+
\vspace{5cm}
46+
47+
{\small wtorek, 11\textsuperscript{15}-14\textsuperscript{00} TN}\\
48+
Mgr inż. Przemysław Świercz
49+
\end{flushright}
50+
\vspace*{\stretch{6}}
51+
\begin{center}
52+
\@date
53+
\end{center}
54+
\end{titlepage}%
55+
}
56+
\makeatother
57+
\author{Justyna Skalska, 225942}
58+
\title{Sprawozdanie nr 6\\
59+
(Crackme - modyfikacja plików binarnych)}
60+
61+
\begin{document}
62+
\floatsetup[listing]{style=Plaintop}
63+
64+
\maketitle
65+
66+
\tableofcontents
67+
\newpage
68+
\listoflistings
69+
\newpage
70+
% \listoffigures
71+
% \newpage
72+
73+
\section{Omówienie tematu}
74+
\subsection{Wstęp}
75+
Naszym zadaniem podczas laboratorium było zmodyfikowanie pliku binarnego, który otrzymaliśmy od prowadzącego. Program prosił o podanie klucza w wyznaczonym czasie, żeby uzyskać wiadomość oznaczającą poprawne dostanie się do systemu. Należało zapoznać się z kodem assemblerowym aplikacji i zmodyfikować ją tak, żeby wiadomość powiadamiająca o sukcesie pojawiała się nawet mimo podania złego klucza. Należało znaleźć dwa sposoby na obejście tego zabezpieczenia. Kolejnym zdaniem było obejście ograniczenia czasowego na podanie klucza. Trzeba było znaleźć dwa sposoby na poradzenie sobie z tym. Ostatnim zadaniem było stworzenie aplikacji, która zmieniała wartość zapisaną pod podanym adresem w pliku binarnym na nową, podaną przez użytkownika.
76+
77+
\subsection{Modyfikacja plików binarnych}
78+
Pliki binarne mają swoją strukturę. Każda wartość w pliku binarnym ma ustalone znaczenie. Sposób ułożenia danych w takim pliku nazywa się formatem pliku. Bloki danych, które można wyodrębnić określa się jako sekcja. Często spotyka się na początku pliku informacje, które pozwalają ustalić sposób ułożenia danych w dalszej części pliku. Takie dane początkowe to nagłówek pliku.~\cite{pliki-binarne} Można z niego odczytać położenie poszczególnych sekcji pliku oraz ich długość, a także punkt wejścia (ang. \textit{entry point}). Dzięki temu możemy odnaleźć sekcji i bajty, które chcemy zmodyfikować. Można to zrobić wykorzystując jakiś edytor heksadecymalny (ang. \textit{hex editor}) nazywany też szesnastkowym. Pozwala on wyświetlić zawartość pliku binarnego w formie tablicy bajtów. Wartość każdego bajtu jest pokazywana jako liczba w systemie szesnastkowym od wartości 00 do FF (dziesiętnie: od 0 do 255).~\cite{pliki-binarne}
79+
80+
\section{Omówienie rozwiązania}
81+
\subsection{Zabezpieczenie kodem}
82+
W funkcji \mintinline{bash}{init} wykorzystywanej na początku funkcji \mintinline{asm}{main} znajduje się fragment, który inicjalizuje tablicę 42 adresami do funkcji \mintinline{asm}{looser} oraz 1 adresem funkcji \mintinline{asm}{winner}. Później w funkcji \mintinline{asm}{main} wywoływana jest funkcja zapisana pod jednym z adresów w tablicy.
83+
84+
\begin{listing}[H]
85+
\caption{Funkcja looser}
86+
\begin{minted}{bash}
87+
08048552 <looser>:
88+
8048552: 55 push %ebp
89+
8048553: 89 e5 mov %esp,%ebp
90+
8048555: 83 ec 18 sub $0x18,%esp
91+
8048558: c7 04 24 63 87 04 08 movl $0x8048763,(%esp)
92+
804855f: e8 74 fe ff ff call 80483d8 <puts@plt>
93+
8048564: c9 leave
94+
8048565: c3 ret
95+
\end{minted}
96+
\end{listing}
97+
98+
\begin{listing}[H]
99+
\caption{Funkcja winner}
100+
\begin{minted}{bash}
101+
08048566 <winner>:
102+
8048566: 55 push %ebp
103+
8048567: 89 e5 mov %esp,%ebp
104+
8048569: 83 ec 18 sub $0x18,%esp
105+
804856c: c7 04 24 78 87 04 08 movl $0x8048778,(%esp)
106+
8048573: e8 60 fe ff ff call 80483d8 <puts@plt>
107+
8048578: c9 leave
108+
8048579: c3 ret
109+
\end{minted}
110+
\end{listing}
111+
112+
\subsubsection{Sposób nr 1}
113+
Aby mieć pewność, że zawsze wywołamy funkcję \mintinline{asm}{winner} można tablicę w \mintinline{asm}{init} wypełnić tylko adresem funkcji \mintinline{asm}{winner}. Na początku należało odczytać z objdump adres bajtów, które chcemy podmienić. Adres funkcji \mintinline{asm}{winner} wynosi 0x8048566, a funkcji \mintinline{asm}{looser} 0x8048552. Wychodzi na to że wystarczy podmienić jeden bajt danych adresu, 0x52 na 0x66. Następnym krokiem było znalezienie adresu bajtu wynoszącego 0x52. Z objdump wynika, że jest to 0x0804858d. Ostatnim krokiem było przejście do strony \url{https://hexed.it}, załadowanie modyfikowanego pliku binarnego i obliczenie szukanego adresu. Podczas liczenia adresu skorzystałam z przedstawionego poniżej równania:
114+
\begin{equation*}
115+
file\_offset = virtual\_address - entry\_point\_address + section\_offset
116+
\end{equation*}
117+
118+
\noindent Dane odczytane z nagłówka ELF oraz objdump:
119+
\begin{itemize}
120+
\item 0x0804858d - adres wirtualny,
121+
\item 0x3f0 - offset sekcji \mintinline{asm}{.text},
122+
\item 0x080483f0 - adres punktu wejścia.
123+
\end{itemize}
124+
125+
\noindent Przykład obliczania wcześniej wspomnianego offsetu dla adresu 0x0804858d:
126+
\begin{equation*}
127+
0x58d = 0x0804858d - 0x080483f0 + 0x3f0
128+
\end{equation*}
129+
130+
\noindent Po zamianie bajtu danych wyeksportowałam zmodyfikowaną aplikację i przetestowałam. Udało mi się uzyskać dostęp do aplikacji i wyświetlić tekst z funkcji \mintinline{asm}{winner}.
131+
132+
\subsubsection{Sposób nr 2}
133+
Kolejnym sposobem obejścia zabezpieczeń kodu była modyfikacja funkcji \mintinline{asm}{looser}. Zamiast wyświetlać normalny komunikat, można zastąpić go komunikatem z funkcji \mintinline{asm}{winner}. Wiadomości z obu tych funkcji zapisane były pod dwoma adresami 0x8048763 dla \mintinline{asm}{looser} oraz 0x8048778 dla \mintinline{asm}{winner}. Należało podmienić tylko jeden bajt adresu w funkcji \mintinline{asm}{looser} z 0x63 na 0x78. Obliczanie offsetu wykonałam analogicznie do pierwszego przykładu. Po pobraniu zmodyfikowanego pliku i przetestowaniu udało się uzyskać pożądany efekt, czyli wyświetlenie komunikatu z funkcji \mintinline{asm}{winner}.
134+
135+
\subsection{Ograniczenie czasowe}
136+
W aplikacji istnieje ograniczenie czasowe, które uniemożliwi nam poprawną weryfikację kodu, gdy licznik osiągnie odpowiednią wartość. Kolejnym zadaniem było obejście tego ograniczenia w taki sposób, żeby klucz można było wprowadzić w dowolnym momencie życia aplikacji.
137+
138+
\begin{listing}[H]
139+
\caption{Funkcja time\_guard}
140+
\begin{minted}{bash}
141+
080484a4 <time_guard>:
142+
80484a4: 55 push %ebp
143+
80484a5: 89 e5 mov %esp,%ebp
144+
80484a7: 83 ec 18 sub $0x18,%esp
145+
80484aa: c7 04 24 00 00 00 00 movl $0x0,(%esp)
146+
80484b1: e8 02 ff ff ff call 80483b8 <time@plt>
147+
80484b6: 8b 15 20 a0 04 08 mov 0x804a020,%edx
148+
80484bc: 29 d0 sub %edx,%eax
149+
80484be: 83 f8 02 cmp $0x2,%eax
150+
80484c1: 7e 07 jle 80484ca<time_guard+0x26>
151+
80484c3: b8 ff ff ff ff mov $0xffffffff,%eax
152+
80484c8: eb 16 jmp 80484e0<time_guard+0x3c>
153+
80484ca: c7 04 24 00 00 00 00 movl $0x0,(%esp)
154+
80484d1: e8 e2 fe ff ff call 80483b8 <time@plt>
155+
80484d6: a3 20 a0 04 08 mov %eax,0x804a020
156+
80484db: b8 00 00 00 00 mov $0x0,%eax
157+
80484e0: c9 leave
158+
80484e1: c3 ret
159+
\end{minted}
160+
\end{listing}
161+
162+
\subsubsection{Sposób nr 1}
163+
Pierwszym sposobem jest modyfikacja funkcji \mintinline{asm}{time_guard}. Zwraca ona 0 jeśli czas, który minął od początku życia aplikacji jest mniejszy niż zadany w kodzie, jeśli jest większy zwracana jest wtedy inna liczba różna od 0. Funkcja jest tak zbudowana, że w przypadku przekroczenia czasu program skacze do komendy \mintinline{asm}{leave} i kończy wykonywanie funkcji zwracając wartość różną od 0. Wystarczyło podmienić offset w funkcji skoku z 0x16 nad 0x11, żeby skoczyć o jedną instrukcję (5 bajtów) wcześniej i przypisywać 0 do \mintinline{bash}{eax} nawet w przypadku przekroczenia limitu czasu. Po wyeksportowaniu zmodyfikowanego pliku i przetestowaniu okazało się, że zmiana zadziałała poprawnie i limit czasu został usunięty.
164+
165+
\subsubsection{Sposób nr 2}
166+
Kolejnym sposobem na ominięcie zabezpieczenia było zmodyfikowane funkcji \mintinline{asm}{main}. Po każdym wywołaniu \mintinline{asm}{time_guard} następowało sprawdzanie czy zwrócona przez funkcję wartość wynosi 0. Jeśli nie to program przechodził dalej i wywoływał funkcję \mintinline{asm}{cheater}, co prowadziło do wywołania funkcji \mintinline{asm}{looser}. W przeciwnym wypadku program przeskakiwał wywołanie \mintinline{asm}{cheater}. Wystarczyło podmienić skok warunkowy na skok, który wykonywał się zawsze w dwóch miejscach. Pierwsze wywołanie funkcji \mintinline{asm}{time_guard} można było zostawić bez zmian, ponieważ wykonywało się ono przed przyjęciem danych od użytkownika, więc bez jakiekolwiek opóźnienia. Podmieniłam kod 0x74 skoku warunkowego \mintinline{asm}{je} na kod 0xeb zwykłego skoku. Po pobraniu zmodyfikowanej aplikacji o przetestowaniu okazało się, że zmiana zadziałała poprawnie i limit czasu został usunięty.
167+
168+
\subsection{Aplikacja do modyfikowania plików binarnych}
169+
Do wykonania ostatniego zadania wykorzystałam język C oraz bibliotekę \textit{elf.h}, stworzoną do manipulacji plikami ELF.
170+
171+
\begin{listing}[H]
172+
\caption{Główna funkcja program do manipulacji plikami binarnymi.}
173+
\begin{minted}{c}
174+
int main(int argc, const char *argv[]) {
175+
if (argc < 3) {
176+
ERROR("Not enough arguments provided!");
177+
return 0;
178+
}
179+
180+
printf("Enter a hexadecimal address: ");
181+
unsigned int address = read_hex_value(8);
182+
printf("Enter a hexadecimal value: ");
183+
unsigned int value = read_hex_value(2);
184+
185+
FILE *input_file = fopen(argv[1], "rb");
186+
if (input_file == NULL) {
187+
fprintf(stderr, "Unable to open '%s': %s\n", argv[1], strerror(errno));
188+
exit(EXIT_FAILURE);
189+
}
190+
191+
int fd = fileno(input_file);
192+
struct stat stat_buffer;
193+
fstat(fd, &stat_buffer);
194+
char *fbase = mmap(NULL, stat_buffer.st_size, PROT_READ, MAP_SHARED, fd, 0);
195+
\end{minted}
196+
\end{listing}
197+
\begin{listing}[H]
198+
\begin{minted}{c}
199+
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) fbase;
200+
unsigned int entry_point = ehdr->e_entry;
201+
unsigned int text_section_offset;
202+
203+
if (!elf_check_file(ehdr)) {
204+
fprintf(stderr, "'%s' is not an ELF file\n", argv[1]);
205+
fclose(input_file);
206+
return 0;
207+
}
208+
209+
Elf32_Shdr *sects = (Elf32_Shdr *) elf_sheader(fbase, ehdr);
210+
char *shstrtab = elf_str_table(fbase, ehdr);
211+
for (int i = 0; i < ehdr->e_shnum; i++) {
212+
if (!strcmp(shstrtab + sects[i].sh_name, ".text")) {
213+
text_section_offset = sects[i].sh_offset;
214+
}
215+
}
216+
217+
long file_length = get_file_length(input_file);
218+
char *binary = read_binary_file(input_file, file_length);
219+
unsigned int calculated_offset = address - entry_point + text_section_offset;
220+
221+
printf("Actual value: %hhX\n", binary[calculated_offset]);
222+
binary[calculated_offset] = value;
223+
printf("New value: %hhX\n", binary[calculated_offset]);
224+
225+
FILE *output_file = fopen(argv[2], "wb");
226+
if (output_file == NULL) {
227+
fprintf(stderr, "Unable to create '%s': %s\n", argv[2], strerror(errno));
228+
exit(EXIT_FAILURE);
229+
}
230+
231+
write_binary_file(output_file, file_length, binary);
232+
233+
fclose(output_file);
234+
fclose(input_file);
235+
return 0;
236+
}
237+
\end{minted}
238+
\end{listing}
239+
240+
Stworzony przeze mnie program przyjmuje dwa argumenty. Plik przeznaczony do modyfikacji oraz nazwę pliku, do którego chcemy zapisać zmodyfikowany pierwszy plik. Po starcie aplikacja pyta o dwie wartości. Pierwszą z nich jest adres, który chcemy zmodyfikować, a drugim wartość, którą chcemy wpisać pod podany wcześniej adres. Obie wartości należy podać w systemie szesnastkowym bez wiodącego 0x.
241+
242+
\section{Wnioski}
243+
Podczas zajęć udało mi się wykonać wszystkie zadania. Zabezpieczenie kodem oraz ograniczenie czasowe aplikacji można było obejść na wiele różnych sposób, jedna są bardziej eleganckie od innych. Przedstawiłam tylko po dwóch wybranych przykładach. Ograniczenie czasowe można było także obejść wstawiając komendę NOP w odpowiednich miejscach, aby nie dopuścić do ich wykonania (np. wywołanie funkcji \mintinline{asm}{cheater}). Dzięki tym zajęciom dowiedziałam się jak w prosty sposób można odczytać kod aplikacji i manipulować nim według własnego uznania. Wykorzystanie tej wiedzy w większych programach może być problematyczne, ponieważ wymaga analizowania i zrozumienia kodu assemblera.
244+
245+
Trudno jest zapobiec tego typu atakom, ponieważ modyfikowany jest tu kod binarny już skompilowanego programu i nie wykonuje się tu wstrzykiwania własnych instrukcji do aplikacji w trakcie jej wykonywania. Jednym ze sposobów protekcji jest użycie wartości sumy kontrolnej dla całej aplikacji. Ta metoda pozwala wykryć modyfikacje obrazu wykonywalnego. Potrzeba do tego programu rozruchowego, który przechowuje prawidłową sumę kontrolną dla wybranego obrazu binarnego. Sam program rozruchowy powinien znajdować się w nieulotnej, chronionej pamięci, najlepiej w samym mikroprocesorze, jeśli ma wbudowaną pamięć ROM jakiegoś typu.~\cite{protecting-binary-executables} Często także stosowane są procedury utrudniające debugowanie i deasemblację aplikacji. Stosowane jest także zaciemnianie kodu (ang. \textit{obfuscation}), które znacząco utrudnia jego zrozumienie. Ma ono na celu utrudnienie inżynierii wstecznej programu.
246+
\newpage
247+
\bibliographystyle{unsrt}
248+
\bibliography{references}
249+
250+
\end{document}

BSiUI2/Lab6/references.bib

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@misc{pliki-binarne,
2+
author = {Dawid Farbaniec},
3+
title = {Podstawy edycji plików binarnych},
4+
year = 2018,
5+
url = {https://haker.info/blog/details/podstawy-edycji-plikow-binarnych},
6+
howpublished = {\url{https://haker.info/blog/details/podstawy-edycji-plikow-binarnych}},
7+
note = {Odwiedzona: 2019-12-04}
8+
}
9+
@misc{protecting-binary-executables,
10+
author = {Embedded Staff},
11+
title = {Protecting Binary Executables},
12+
year = 2000,
13+
url = {https://www.embedded.com/protecting-binary-executables/},
14+
howpublished = {\url{https://www.embedded.com/protecting-binary-executables/}},
15+
note = {Odwiedzona: 2019-12-04}
16+
}

0 commit comments

Comments
 (0)