avatar

目录
BJD3rd Test your nc 出题笔记 | DASCTF 安恒5月赛

这几天看了各位师傅发的wp,发现居然没有用预期解做Test your nc这道题的,所以就发篇文章略微讲解一下叭~

Test your nc

如题,使用nc连接上题目就能拿到一个shell,但这显然不是本题的考点。在接下来尝试cat flag时,冒出来一只小脑斧,然后就卡住了:

ls -al查看一下文件才发现flag居然有15T之大

bash
1
2
3
4
5
6
7
8
9
$ ls -al
total 64
drwxr-x--- 1 root ctf 4096 May 14 07:47 .
drwxr-xr-x 1 root root 4096 May 12 06:26 ..
-rwxr-x--- 1 root ctf 220 May 5 2019 .bash_logout
-rwxr-x--- 1 root ctf 3771 May 5 2019 .bashrc
-rwxr-x--- 1 root ctf 807 May 5 2019 .profile
-rw-r----- 1 root ctf 15429670010889 May 14 07:47 flag
-rw-r----- 1 root ctf 23 May 12 06:42 readme

显然是直接读不出来的。此外readme文件中给出了如下提示:

flag is hidden in file

15Tflag文件啊,磁盘都没这么大,所以肯定是用脚本生成的。于是用ps命令查看一下系统进程,找找线索。

发现/start.sh在后台运行:

bash
1
2
3
4
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2600 100 ? Ss May14 0:00 /bin/sh /start.sh
root 18 0.0 0.0 2500 68 ? S May14 0:00 sleep infinity

查看其内容,发现位于根目录的/f1a9.py用于生成flag文件:

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat /start.sh
#!/bin/sh
# Add your startup script

# generate flag
python /f1a9.py
chown root:ctf /home/ctf/flag
chmod 640 /home/ctf/flag /home/ctf/readme

# start ctf-xinetd
/etc/init.d/xinetd start;
trap : TERM INT;
sleep infinity & wait\

在根目录可以找到f1a9.pyf1a9.bak这两个文件:

bash
1
2
3
4
5
6
$ ls -al /
total 72
drwxr-xr-x 1 root root 4096 May 14 07:47 .
drwxr-xr-x 1 root root 4096 May 14 07:47 ..
-rwxr--r-- 1 root root 430 May 13 11:39 f1a9.bak
-rwx------ 1 root root 528 May 13 11:37 f1a9.py

其中f1a9.py是不可读的,但是.bak可读,内容如下:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
#coding=utf-8

from random import randint
from uuid import uuid4

flag = [hex(i)[2:-1] for i in uuid4().fields]

f=open('/home/ctf/flag','w')
f.write('🐀')
f.seek(1024*1024*1024)
f.write('flag{')
offset = 1024
for i in flag:
f.seek(1024*1024*1024*randint(offset,offset+2048))
offset += 2048
f.write(i)
f.write('}')
f.seek(1024*1024*1024*randint(offset,offset+2048))
f.write('good job!')
f.close()

师傅们可以在本地用这个脚本复现题目,生成flag文件

这个脚本用于生成flag,看到这里大家就应该清楚了,之所以flag文件会辣么大,就是因为使用了seek函数,每向文件中写一部分flag后,就将flag文件的偏移量(读写位置)后移动2T。如此一来,在有用的数据之间,形成了一个个由\x00组成的空洞(hole),这些hole是不占用磁盘空间的。

虽然每隔2T就会出现一部分flag,但是由于数据随机分布在0~2T之间,直接爆破读flag费时费力

(好多师傅是直接读的orz)

于是,本题的考点来啦!

既然大部分无用的hole是不占空间,那么有没有办法跳过这些hole直接读取数据呢?

知识点——lseek函数:

off_t lseek(int fd, off_t offset, int whence);

每一个已打开的文件都有一个读写位置,当打开文件时通常其读写位置是指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或write()时,读写位置会随之增加,lseek()便是用来控制该文件的读写位置。参数fildes 为已打开的文件描述词,参数offset为根据参数whence来移动读写位置的位移数。

参数 whence 为下列其中一种:

  • SEEK_SET 参数offset 即为新的读写位置。
  • SEEK_CUR 以目前的读写位置往后增加offset 个位移量。
  • SEEK_END 将读写位置指向文件尾后再增加offset 个位移量。

当whence 值为SEEK_CUR 或SEEK_END 时,参数offset 允许负值的出现。

除了上述的三个whence外,Linux还支持以下两种新模式:

Seeking file data and holes Since version 3.1, Linux supports the following additional values for whence:

  • SEEK_DATA Adjust the file offset to the next location in the file greater than or equal to offset containing data. If offset points to data, then the file offset is set to offset.

  • SEEK_HOLE Adjust the file offset to the next hole in the file greater than or equal to offset. If offset points into the middle of a hole, then the file offset is set to offset. If there is no hole past offset, then the file offset is adjusted to the end of the file (i.e., there is an implicit hole at the end of any file).

其中SEEK_DATA是跳过hole寻找下一个数据,SEEK_HOLE是跳过数据寻找下一个hole。

这题用SEEK_DATA这个新模式即可快速的读出flag

lseek是C语言的函数,莫非要编程实现读取flag?看来得找个地方敲个C代码,然后编译执行。

然鹅为了防止搅shi,无情的出题人把唯一可写的目录/tmp禁用了,所以说C语言基本上是不能用了。

不过在执行ps命令的时候(还有查看/start.sh时),不难发现系统中是有python命令的:

bash
1
2
3
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
ctf 4937 0.0 0.0 2720 900 ? S 07:43 0:00 /usr/bin/timeout 15 /usr/bin/python -c import os; [os.system(raw_input('$ ')) for i in range(10)]

同样为了防止搅shi,出题人设置了15秒的限时。不过可以通过kill命令杀死timeout进程,实现不限时的使用shell(爆破flag的师傅大概是这么做的叭orz)

从这个进程可以看出,我们nc得到shell是其实通过pythonos.system()实现的,而且只能执行10条命令。

此外,通过python -c xxxx可以执行python代码,且pythonos模块中有和C语言lseek功能相同的函数:

python
1
2
3
4
5
6
7
8
9
>>> help(os.lseek)

Help on built-in function lseek in module posix:

lseek(...)
lseek(fd, pos, how) -> newpos

Set the current position of a file descriptor.
Return the new cursor position in bytes, starting from the beginning.

最终读取flagpython代码如下:

python
1
2
3
4
5
6
7
8
9
10
11
import os
fd = os.open('/home/ctf/flag',0)
off = 0
flag = ''
while off < 15429670010889:
off = os.lseek(fd,off,3) # SEEK_DATA
off += 1
a = os.read(fd,1)
if a != '\x00':
flag += a
print flag

通过python -c 执行代码:

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ bash
python -c """
import os
fd = os.open('/home/ctf/flag',0)
off = 0
flag = ''
while off < 15429670010889:
off = os.lseek(fd,off,3) # SEEK_DATA
off += 1
a = os.read(fd,1)
if a != '\x00':
flag += a
print flag
"""
🐀flag{56463733871e403a84db353acc08b552}good job!

嘎嘎嘎 (o゚v゚)ノ

文章作者: TaQini
文章链接: http://taqini.space/2020/05/28/bjd3rd-testyournc/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 TaQini
打赏
  • Wechat
    Wechat
  • Alipay
    Alipay

评论