0%

论菜鸡pwn手如何在无网环境(ps:类似国赛)下生存

论菜鸡pwn手如何在无网环境(ps:类似国赛)下生存

本文首发于先知社区

引言:在打完一次无网环境后,觉得没网环境实在难受,查个libc都没得查。。没准备好,那时碰巧我下了ctf-challenge,在那里碰巧弄到了libc,可能有人喜欢用libc-searcher那个py版本的项目,我不怎么喜欢,用那个导入库查找感觉较慢,还是喜欢手动泄露后到网页查找,于是有了这篇文章

pwntools安装

1
pip install pwntools

出错自己解决啊,那些个错误都查得到

one_gadget

1
gem install one_gadget

gdb配置

我个人觉得,peda和pwndbg必备,gef也可以用上,随你
自己写了个脚本,很渣,自选用不用
项目地址
三个插件一起用会冲突,一部分功能失效,建议注释掉.gdbinit里的gef部分

welpwn

这个项目是国防科技大学弄的,挺好用的,最主要是能加载libc

1
git clone https://github.com/matrix1001/welpwn

到项目目录下然后

1
sudo python setup.py install

用法你可以看他项目里的介绍

ctf-wiki本地搭建

作为一个不是啥都熟的选手,ctf-wiki还是必备的,打比赛的时候查查exp,查查用法什么都好

首先安装docker

这个不讲了

docker pull镜像

1
2
3
docker search ctf  #先查找镜像,镜像名知道可以不查找
docker pull ctfwiki/ctf-wiki #pull ctfwiki镜像
docker run -d --name=ctf-wiki -p 4100:80 ctfwiki/ctf-wiki #-d参数为后台运行,--name为名称 -p为端口映射 4100是本地端口,80是docker端口

部署libc-database

1
2
docker pull blukat29/libc
docker run -p 4101:80 -d blukat29/libc

配置文件目录/etc/nginx/conf.d/nginx.conf
启动端口在4101,这里有个小问题,就是无法下载,解决方法,替换nginx的配置文件为如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
location / {
try_files $uri @app;
}
location @app {
include uwsgi_params;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
location /static {
alias /app/static;
}
location /d {
alias /libc-database/db;

location ~ \.symbols$ {
default_type text/plain;
}
}
}

最主要是

1
2
3
4
5
6
7
location /d {
alias /libc-database/db;

location ~ \.symbols$ {
default_type text/plain;
}
}

然后发觉每次重启都会他配置文件都会重置,研究下了他docker里的东西,发觉是entrypoint.sh影响了,所以修改下entrypoint.sh就行

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
#! /usr/bin/env bash
set -e
# Get the maximum upload file size for Nginx, default to 0: unlimited
USE_NGINX_MAX_UPLOAD=${NGINX_MAX_UPLOAD:-0}
# Generate Nginx config for maximum upload file size
echo "client_max_body_size $USE_NGINX_MAX_UPLOAD;" > /etc/nginx/conf.d/upload.conf

# Get the number of workers for Nginx, default to 1
USE_NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-1}
# Modify the number of worker processes in Nginx config
sed -i "/worker_processes\s/c\worker_processes ${USE_NGINX_WORKER_PROCESSES};" /etc/nginx/nginx.conf

# Get the URL for static files from the environment variable
USE_STATIC_URL=${STATIC_URL:-'/static'}
# Get the absolute path of the static files from the environment variable
USE_STATIC_PATH=${STATIC_PATH:-'/app/static'}
# Get the listen port for Nginx, default to 80
USE_LISTEN_PORT=${LISTEN_PORT:-80}

# Generate Nginx config first part using the environment variables
echo "server {
listen 80;
location / {
try_files \$uri @app;
}
location @app {
include uwsgi_params;
uwsgi_param Host \$host;
uwsgi_param X-Real-IP \$remote_addr;
uwsgi_param X-Forwarded-For \$proxy_add_x_forwarded_for;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
location /static {
alias /app/static;
}
location /d {
alias /libc-database/db;

location ~ \.symbols$ {
default_type text/plain;
}
}" > /etc/nginx/conf.d/nginx.conf

# If STATIC_INDEX is 1, serve / with /static/index.html directly (or the static URL configured)
if [[ $STATIC_INDEX == 1 ]] ; then
echo " location = / {
index $USE_STATIC_URL/index.html;
}" >> /etc/nginx/conf.d/nginx.conf
fi
# Finish the Nginx config file
echo "}" >> /etc/nginx/conf.d/nginx.conf

exec "$@"

然后就部署完成了,如果还嫌麻烦,可以用下我这个Dockerfile
项目地址
用法在项目里已经说明了,只是修改了一点点错误

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>libc database search</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/start/jquery-ui.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="{{ url_for('static', filename='bootstrap.min.js') }}" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="{{ url_for('static', filename='jquery-ui.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='URI.min.js') }}"></script>
</head>
<body>
<div class="container">
<div class="page-header">
<div class="pull-right">
<div>
View source <a href="https://github.com/blukat29/search-libc">here</a>
</div>
<div>
Powered by <a href="https://github.com/niklasb/libc-database">libc-database</a>
</div>
</div>
<h1>libc database search</h1>
</div>
<div class="row">

<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h1 class="panel-title panel-title-inline">Query</h1>
<span class="panel-title-right">
<a href="/?q=_rtld_global%3A0">show all libs</a>
/
<a href="/">start over</a>
</span>
</div>
<div class="panel-body">
<div id="queries"></div>
<form class="form-inline">
<button type="button" id="query-add" class="btn btn-primary">+</button>
<button type="button" id="query-find" class="btn btn-success">Find</button>
</form>
</div>
</div>
</div>

<div class="col-sm-6">

{% if libs %}
<div class="panel panel-default">
<div class="panel-heading"><h1 class="panel-title">Matches</h1></div>
<div class="panel-body">
{% for lib_name in libs %}
<div>
<a class="lib-item {{ 'active' if lib_name == lib }}">
{{ lib_name }}
</a>
</div>
{% endfor %}
</div>
</div>
{% endif %}

{% if symbols %}
<div class="panel panel-default">
<div class="panel-heading">
<h1 class="panel-title panel-title-inline">{{ lib }}</h1>
<a class="panel-title-right" href="/d/{{ lib }}.so">Download</a>
</div>
<div class="panel-body">
<table class="table table-condensed table-hover">
<tr>
<th></th>
<th>Symbol</th>
<th>Offset</th>
<th>Difference</th>
</tr>
{% for name, ofs in symbols %}
<tr class="mono-font symbol-row">
<td><input type="radio" name="choose-base" class="symbol-radio" /></td>
<td class="symbol-name">{{ name }}</td>
<td class="symbol-ofs">{{ '0x%06x' | format(ofs) }}</td>
<td class="symbol-diff"></td>
<tr>
{% endfor %}
</table>
<div>
<a target="_blank" href="/d/{{ lib }}.symbols">All symbols</a>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>

<footer class="footer">
<div class="container">
<span class="text-muted">Database updated on May 23, 2018.</span>
</div>
</footer>

<script type="text/javascript" src="{{ url_for('static', filename='main.js') }}"></script>
<script type="text/javascript">
{% for name, addr in query.items() %}
add_query("{{ name }}", "{{ addr }}");
{% endfor %}
</script>
<script type="text/javascript" src="{{ url_for('static', filename='names.js') }}"></script>
<script type="text/javascript">
set_autocomplete(names);
</script>
</body>
</html>

本地搭建ctf-all-in-one

https://github.com/firmianay/CTF-All-In-One

下载项目

git clone https://github.com/firmianay/CTF-All-In-One

GitBook基础

README.md和SUMMARY.md
必备文件

利用Docker安装Gitbook

1
2
docker search gitbook #可以查看,我用了最高星的那个
docker pull fellah/gitbook

先到项目的目录下
然后

1
docker run -v $PWD:/srv/gitbook -v $PWD/html:/srv/html fellah/gitbook gitbook build . /srv/html

展示Gitbook文件

因为生成的是html静态页面所以需要一个web服务来显示,我用nginx

1
2
docker pull nginx
docker run --name ctf-all-in-one -v /$PWD/html:/usr/share/nginx/html -d -p 4102:80 nginx

这样就搭建完成了

访问你自己的4100-4102端口看下,是否成功了

下载ctf-challenge,例题以及exp

git clone https://github.com/ctf-wiki/ctf-challenges
这个说不定哪天就用上了,说不准

exp模板构建

因为要快速做题,每次重复的部分整合起来比较好,所以便研究下exp模板如何弄,我是通过修改pwntools里自带的exp模板,生成自己专属的模板的,我建议都弄成自己的exp模板类型吧,因为每个人代码风格不一样,没必要一定用大佬的模板,当然有可能大佬的模板好,但不一定适合自己

模板位置

第一个是模板的具体内容,第二个是生成模板的方法

  • pwnup.mako
  • template.py

你可以find / -name 名称
找到具体位置,最好先备份一份

具体配置自行配置,因为每个人风格不同

pwntools exp模板自定义

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<%page args="binary, heap=None, link=None, libc=None, local=True"/>\
<%
import os
import sys

from pwnlib.context import context as ctx
from pwnlib.elf.elf import ELF
from pwnlib.util.sh_string import sh_string
from elftools.common.exceptions import ELFError

argv = list(sys.argv)
argv[0] = os.path.basename(argv[0])

try:
if binary:
ctx.binary = ELF(binary, checksec=False)
except ELFError:
pass

if not binary:
binary = '/path/to/binary'

exe = os.path.basename(binary)
if binary[0] != '/':
binary = "./" + binary
binary_repr = repr(binary)
libc_repr = repr(libc)
%>\
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *


local = 1
%if link is not None:
link = "${link}"
%else:
link = ''
%endif
host,port = map(str.strip, link.split(':')) if link != '' else ("",0)
context.log_level = 'debug'
#context.terminal = "/home/noone/hyperpwn/hyperpwn-client.sh"
context.terminal = ['mate-terminal','--geometry=94x60--10-26','--hide-menubar', '-x','sh','-c',]
%if ctx.binary:
exe = ${binary_repr}
context.binary = exe
elf = ELF(exe)
libc = elf.libc
<% binary_repr = 'exe.path' %>
%else:
context.update(arch='i386')
exe = ${binary_repr}
<% binary_repr = 'exe' %>
%endif


#don't forget to change it
if local:
io = process(exe)
else:
io = remote(host,port)

s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
rl = lambda : io.recvline().strip()
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s--> 0x%x\033[0m'%(s,addr))
ga = lambda job="" : gdb.attach(io, job) if local else 0
ia = lambda : io.interactive()


# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
gdb.attach(io,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(io,"b *{}".format(hex(addr)))

# get_one_gadget
def get_one_gadget(filename):
try:
import subprocess
except Exception as e:
print("subprocess not install")
exit(0)
return map(int, subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))




#===========================================================
# EXPLOIT GOES HERE
#===========================================================

%if ctx.binary and not quiet:
# ${'%-10s%s-%s-%s' % ('Arch:',
ctx.binary.arch,
ctx.binary.bits,
ctx.binary.endian)}
%for line in ctx.binary.checksec(color=False).splitlines():
# ${line}
%endfor
%endif


%if heap is not None:
def choice(idx):
def wrap(f):
def go(*args, **kargs):
sla(":", idx)
f(*args, **kargs)
return go
return wrap

@choice(idx=1)
def new():
pass

@choice(idx=2)
def edit(idx):
pass

@choice(idx=3)
def show(idx):
pass

@choice(idx=4)
def delete(idx):
pass
%endif

def exp(host, rce=False):
if rce:
one_gadget = get_one_gadget(libc.path)



if local:
leak_name = "__libc_start_main"
leak_addr = 0x1
lg("leak_addr", leak_addr)
libc_base = leak_addr - libc.sym[leak_name]
bin_sh_addr = libc_base + libc.search('/bin/sh').next()
system_addr = libc_base + libc.sym['system']
malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
else:
try:
from LibcSearcher import *
obj = LibcSearcher(leak_name, leak_addr)
libc_base = leak_addr - obj.dump(leak_name)
system_addr = libc_base + obj.dump("system")
malloc_hook = libc_base + obj.dump("__malloc_hook")
free_hook = libc_base + obj.dump("__free_hook")
bin_sh_addr = libc_base + obj.dump("str_bin_sh")
except Exception as e:
print("LibcSearcher not install")
exit(0)
lg("libc", libc_base)

ia()


if __name__ == '__main__':
exp(host,)

awd模板

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<%page args="binary, host=None, port=None, libc=None, local=True"/>\
<%
import os
import sys

from pwnlib.context import context as ctx
from pwnlib.elf.elf import ELF
from pwnlib.util.sh_string import sh_string
from elftools.common.exceptions import ELFError

argv = list(sys.argv)
argv[0] = os.path.basename(argv[0])

try:
if binary:
ctx.binary = ELF(binary, checksec=False)
except ELFError:
pass

if not binary:
binary = '/path/to/binary'

exe = os.path.basename(binary)
if binary[0] != '/':
binary = "./" + binary
binary_repr = repr(binary)
libc_repr = repr(libc)
%>\
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
import subprocess

local = 1
%if host:
host = args.HOST or ${repr(host)}
%else:
iplist = [
'127.0.0.1'
]
%endif
%if port:
port = int(args.PORT or ${port})
%else:
port = 10000
%endif
context.log_level = 'debug'
%if ctx.binary:
exe = ${binary_repr}
context.binary = exe
elf = ELF(exe)
libc = elf.libc
<% binary_repr = 'exe.path' %>
%else:
context.update(arch='i386')
exe = ${binary_repr}
<% binary_repr = 'exe' %>
%endif









def exp(host):
if local:
io = process(exe)
else:
io = remote(host,port, timeout=10)
#don't forget to change it
s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
gdb.attach(io,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(io,"b *{}".format(hex(addr)))

# get_one_gadget
def one_gadget(filename):
return map(int, subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))
#one_gadget = one_gadget(libc.path)
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
%if ctx.binary and not quiet:
${'%10s %s-%s-%s' % (' # Arch:',
ctx.binary.arch,
ctx.binary.bits,
ctx.binary.endian)}
%for line in ctx.binary.checksec(color=False).splitlines():
# ${line}
%endfor
%endif




io.interactive()
'''
sl("cat *flag*")
try:
flag = rp(1)
import re
flag = re.findall("flag{.*?}", flag)[0]
print("flag is: " + flag)
except Exception as e:
print(e)
'''

def thread_attack_all():
import threading
threads = []
for ip in iplist:
threads.append(threading.Thread(target=exp, args=(ip,)))
for thread in threads:
thread.start()
thread.join()

def process_attack_all():
import multiprocessing
pool = multiprocessing.Pool(processes=10)
for ip in iplist:
pool.apply_async(exp, (ip, ))
pool.close()
pool.join()

if __name__ == '__main__':
for ip in iplist:
exp(ip)

具体语法可以理解为
%if args
content
%endif
content就是内容,args就是参数,通过这个方法进行模板的生成

pwntools exp模板生成方法

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
#!/usr/bin/env python2
from __future__ import absolute_import

import re

from pwn import *
from pwnlib.commandline import common

from mako.lookup import TemplateLookup

parser = common.parser_commands.add_parser(
'template',
help = 'Generate an exploit template'
)

parser.add_argument('exe', nargs='?', help='Target binary')
parser.add_argument('--host', help='Remote host / SSH server')
parser.add_argument('--port', help='Remote port / SSH port', type=int)
parser.add_argument('--libc', help='Remote libc version')
parser.add_argument('--local', help='local debug', action='store_true')

def main(args):
cache = None

if cache:
cache = os.path.join(context.cache_dir, 'mako')

lookup = TemplateLookup(
directories = [os.path.join(pwnlib.data.path, 'templates')],
module_directory = cache
)


template = lookup.get_template('pwnup.mako')
output = template.render(args.exe,
args.host,
args.port,
args.libc,
args.local,
)

# Fix Mako formatting bs
output = re.sub('\n\n\n', '\n\n', output)

print output

if not sys.stdout.isatty():
try: os.fchmod(sys.stdout.fileno(), 0700)
except OSError: pass

if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)

最终效果

└──╼ $pwn template oreo –libc libc.so.6

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PwnContext.core import *
local = True

# Set up pwntools for the correct architecture
exe = './' + 'oreo'
elf = context.binary = ELF(exe)

#don't forget to change it
host = '127.0.0.1'
port = 10000

#don't forget to change it
#ctx.binary = './' + 'oreo'
ctx.binary = exe
libc = args.LIBC or 'libc.so.6'
ctx.debug_remote_libc = True
ctx.remote_libc = libc
if local:
context.log_level = 'debug'
try:
io = ctx.start()
except Exception as e:
print(e.args)
print("It can't work,may be it can't load the remote libc!")
print("It will load the local process")
io = process(exe)
else:
io = remote(host,port)
#===========================================================
# EXPLOIT GOES HERE
#===========================================================

# Arch: i386-32-little
# RELRO: No RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: No PIE (0x8048000)
def exp():
pass
if __name__ == '__main__':
exp()
io.interactive()

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/9f664865/
版权声明:转载请注明出处!