尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

奇安信2019校招笔试题(二)解析:安全思维与代码能力全梳理

奇安信2019校招笔试题(二)解析:安全思维与代码能力全梳理 奇安信2019校招笔试题二很多人拿到手第一反应是“怎么还有第二套”。我当时也是这个反应毕竟第一套已经考得够细了结果第二套在安全方向和代码能力上挖得更深。后来回头看这套题其实是把“你会不会写代码”和“你有没有安全思维”两件事一起考了。陆陆续续有不少准备校招的同学问过我这套题考什么这里我结合回忆和同行反馈把里面的考点、解题思路、避坑经验完整梳理一遍给后面要投奇安信或者其他安全公司的小伙伴做个参考。这套题覆盖了网络基础、操作系统、Linux命令、SQL注入、代码审计、日志分析以及三道编程题整体风格偏实战不考死记硬背几乎每道题都在逼你“用原理推导结果”。如果你是准备安全方向校招的或者想看看安全公司笔试到底考什么这篇值得花十分钟读完。1. 整体出题思路与备考方向1.1 题目风格与覆盖范围奇安信的笔试题型并不花哨没有脑筋急转弯也没有故意刁难人的偏题怪题基本就是选择题、简答题、编程题三大块。第二套卷子里选择题大概有十道左右覆盖TCP/IP协议、操作系统进程线程、Linux常用命令、数据库查询以及一部分安全基础常识。简答题集中在漏洞原理和日志分析上编程题则是三道上机题难度从入门到中等偏上。这套题的风格很典型广度优先深度次之。它不要求你在某一个方向成为专家但要求你对整个知识体系有基本认知。比如网络题不会考OSPF的LSA类型这种细节但一定会考TCP三次握手的状态变迁安全题不考具体的CVE编号但会问你SQL注入的判别方式。这种出题思路和奇安信的业务特点有关——安全产品线多从边界安全到数据安全再到安全管理平台都需要工程师既能理解网络又懂系统还能写点自动化脚本所以笔试自然就成了“全栈基础能力检测”。1.2 安全方向笔试的特殊之处和普通后端开发笔试相比安全公司的笔试题有两个明显区别。第一SQL注入和XSS这类Web安全题基本是必考的而且不是考定义而是给你一段真实代码或一段日志让你找出问题、说明原理、给出修复方案。第二代码审计题会占一定比重比如给一段C语言代码让你分析是否存在缓冲区溢出或者给一段PHP代码让你找SQL注入点这种题在普通开发岗笔试里几乎见不到。所以备考的时候不能只刷算法题还得专门补一补常见漏洞的代码形态。我的建议是至少熟练掌握SQL注入字符型、数字型、报错注入、XSS反射型、存储型、CSRF、SSRF、命令注入、缓冲区溢出基本原理。不要求你能写出利用payload但至少要能看懂漏洞代码并说明修复思路。这套题里简答题基本就围绕这几个方向展开。2. 基础选择题经典考点逐个拆2.1 TCP三次握手的状态变化第三题考了TCP建立连接过程中双方的状态变化。题目大概是客户端发送SYN之后进入什么状态服务端收到SYN并回复SYNACK后进入什么状态最后客户端发送ACK后双方进入什么状态。答案本身不复杂客户端发送SYN后进入SYN_SENT服务端收到SYN回复SYNACK后进入SYN_RCVD客户端收到SYNACK后发送ACK并进入ESTABLISHED服务端收到ACK后也进入ESTABLISHED。这里容易踩坑的地方是很多人会搞混服务端的状态名以为服务端回复之后进入SYN_SENT实际上服务端此时处于SYN_RCVD有些教材写作SYN_RECEIVED。这道题想拿分不难但想拿稳需要连为什么一起理解。三次握手的本质是双方确认彼此的收发能力第一次握手客户端告诉服务端“我能发”第二次握手服务端告诉客户端“我能收也能发”第三次握手客户端告诉服务端“我能收”。这样双方都确认了对方的收发能力正常。笔试如果出变体题比如问“为什么需要第三次握手”答案就是防止已失效的连接请求突然传到服务端导致服务端白白建立连接浪费资源。2.2 SQL注入分类与识别另一道印象深刻的选择题是给了一条SQL语句和一个输入参数问属于哪类注入。题目大致是这样的SELECT * FROM users WHERE username $username AND password $password输入的username是admin --。问这是什么类型的注入。答案很明显是字符型注入。常见注入类型就那么几种字符型、数字型、报错注入、布尔盲注、时间盲注、联合查询注入、堆叠注入。字符型注入的特征是参数被单引号包裹攻击者通过闭合单引号来改变SQL结构。数字型注入则直接出现在WHERE id $id这种没有引号的场景里。这类题的做题技巧是看SQL语句里参数有没有被引号包裹有引号就是字符型没引号就是数字型。报错注入的标志是页面显示数据库报错信息布尔盲注是页面返回内容有差异但不显示数据时间盲注是靠延迟判断条件真假。把这些特征整理成一张表选择题基本不会失分。2.3 Linux命令考点Linux题考得比较基础比如给了几个命令选项问哪个能查看端口监听状态。答案是netstat -tlnp或者ss -tlnp。这里的坑在于很多人记得netstat但忘了参数-t是TCP、-l是监听、-n是显示数字地址和端口、-p是显示进程名。面试时也常被追问ss和netstat的区别简单说ss更快因为它直接读取内核socket信息而netstat需要遍历/proc下的文件。还有一个常见题是查看文件末尾内容的命令答案是tail考点是tail -f会持续跟踪文件新增内容这在分析日志时非常常用。不要小看这些Linux命令题实际工作中排查问题全靠它们。我建议把下面这些命令和参数背熟ps -ef、top、netstat -tlnp、ss、lsof -i、tcpdump -i eth0 port 80、grep -v、awk {print $1}、find / -name *.conf。都是高频考点也是工作刚需。2.4 进程与线程操作系统部分考了一道线程与进程的经典题同一进程内的多个线程共享什么不共享什么。共享的是地址空间、全局变量、文件描述符、堆内存不共享的是栈、寄存器、线程ID、程序计数器。很多人在“栈”这个选项上翻车觉得线程都有自己的栈应该算独立资源。这里要从概念上理解每个线程确实有独立的栈空间用来保存局部变量和函数调用信息所以栈是线程私有的不共享。而堆是进程级别的所有线程共享。这道题要是出成多选题问“下列哪些是线程私有的”答案就是栈和寄存器。这类操作系统题其实没什么技巧就是理解加记忆。建议用一张对比表把进程和线程的资源归属理清楚考前过一遍很管用。3. 安全分析题漏洞原理与日志审计3.1 缓冲区溢出原理题简答题第一道给了一段简化版的C代码让分析是否存在安全风险并说明原理。代码大概是这样的#include stdio.h #include string.h void vuln(char *input) { char buf[64]; strcpy(buf, input); printf(%s\n, buf); } int main(int argc, char *argv[]) { if (argc 1) { vuln(argv[1]); } return 0; }这道题考的是缓冲区溢出。strcpy不检查源字符串长度如果input超过64字节就会覆盖栈上buf之后的内存包括返回地址。攻击者精心构造输入可以改写函数返回地址让程序跳转到攻击者指定的代码位置从而执行任意代码。答题时不能只说“有缓冲区溢出漏洞”还要把原理讲透栈布局从高地址到低地址依次是函数参数、返回地址、保存的帧指针、局部变量。buf是局部变量往低地址方向增长而strcpy是往高地址方向填充数据所以数据会从buf开始一路覆盖到返回地址。修复方案有两个方向一是把strcpy换成strncpy并明确指定长度二是用安全函数strcpy_s如果平台支持最根本的做法是避免使用不安全的C字符串函数。这种题在安全公司笔试里出现频率非常高几乎是必考。备考时可以多写几个类似的C代码片段练练手比如gets()、sprintf()、sscanf()这些函数都是缓冲区溢出的重灾区。3.2 代码审计找SQL注入另一道简答题给了一段PHP代码让找出漏洞并写出修复后的代码?php $id $_GET[id]; $sql SELECT * FROM products WHERE id . $id; $result mysqli_query($conn, $sql); ?这道题一眼就能看出是数字型SQL注入因为$id直接拼接进了SQL语句且没有用引号包裹。攻击者传入id1 UNION SELECT username,password FROM users就能把用户表数据带出来。修复方案要从三个层面答第一参数化查询是首选PDO预处理语句可以彻底避免拼接第二输入校验强制要求id必须是整数用filter_var($id, FILTER_VALIDATE_INT)过滤第三最小权限原则数据库连接账号不要用root只给业务所需的最小权限。写修复代码时要注意光写mysqli_real_escape_string不算完美因为转义函数在部分字符集场景下可以被绕过比如宽字节注入。所以答题时建议直接上预处理语句体现专业度。?php $stmt $conn-prepare(SELECT * FROM products WHERE id ?); $stmt-bind_param(i, $id); $stmt-execute(); ?这类代码审计题阅卷老师重点看你能否区分“修补”和“根治”。转义只是修补参数化查询才是根治这个层次感一定要答出来。3.3 日志分析题日志分析题给了一段Nginx访问日志内容大致是这样的192.168.1.10 - - [12/Jan/2019:10:15:23 0800] GET /admin/login.php HTTP/1.1 200 532 192.168.1.10 - - [12/Jan/2019:10:15:24 0800] GET /admin/config.php HTTP/1.1 403 214 192.168.1.10 - - [12/Jan/2019:10:15:26 0800] GET /admin/backup.zip HTTP/1.1 404 153 10.0.0.5 - - [12/Jan/2019:10:16:01 0800] GET /index.php HTTP/1.1 200 1024问哪条请求最可疑为什么。答案要抓两个点一是来源IP192.168.1.10在短时间内连续探测/admin/目录下的多个文件符合目录扫描特征二是路径名backup.zip、config.php这类文件往往是敏感文件攻击者在尝试获取备份和配置信息。虽然这些请求没有全部成功有403和404但行为本身已经构成威胁情报里的“侦察探测”。答题时要把分析思路写清楚先看IP访问频率再看请求路径的规律最后结合返回码判断攻击是否得手。如果要进一步排查用grep 192.168.1.10 access.log提取该IP的所有请求分析UA字段是不是扫描器特征有没有遗漏的高危请求。日志分析题考察的是实战能力没有标准答案但必须展现出“从日志里还原攻击链”的思路。备考时可以多看看常见的目录扫描工具如dirsearch、御剑的访问特征它们往往表现为短时间内大量不同路径的同IP请求且部分路径带有.bak、.sql、.git等敏感后缀。4. 编程题实战三道典型题的完整解法4.1 第一题反转字符串中的单词题目要求给定一个字符串逐个反转其中每个单词的顺序但单词内部字符顺序不变。例如输入hello world输出olleh dlrow。要求空间复杂度O(1)除了存储结果的数组外。这道题不难但考察的是对字符串操作的熟练度。常规思路是遍历字符串遇到空格就把当前单词反转。实现上可以写成def reverse_words(s): chars list(s) n len(chars) start 0 for i in range(n 1): if i n or chars[i] : chars[start:i] reversed(chars[start:i]) start i 1 return .join(chars)笔试时要注意几个边界条件字符串为空、只有一个单词、多个连续空格、字符串尾部有空格。我当年写的时候漏了“连续空格”的情况导致start指针指到空格上反转逻辑出错。后来养成了习惯遇到这类题先列边界条件再动手写。如果要求在原字符串上操作C/C风格思路也一样反转每个单词区间即可。核心代码就是reverse(chars, start, end)注意区间是左闭右开还是左闭右闭别搞混。4.2 第二题IP地址合法性判断题目要求输入一个字符串判断它是否是合法的IPv4地址。合法条件由四个十进制数组成范围0到255数字之间用点分隔不能有前导零有的题目不禁止前导零看题目要求。这题看似简单但边界条件很多。直接写split(.)然后判断每个段是否在0到255之间是不够的因为还要处理1.2.3.4.5段数不对、1.2.3段数不够、01.2.3.4前导零、空字符串、非数字字符等情况。我给出的解法是def is_valid_ip(s): parts s.split(.) if len(parts) ! 4: return False for p in parts: if not p or not p.isdigit(): return False if len(p) 1 and p[0] 0: return False if int(p) 255: return False return True这道题能看出一个人写代码是否严谨。很多人一上来就int(p)判断范围结果int()直接报异常还有人忘记判断len(p) 1把0.0.0.0误判为非法。多花一分钟写几个测试用例比写完整段代码更重要。我当时写了大概七八个测试用例包括0.0.0.0、255.255.255.255、256.1.1.1、01.2.3.4、1.2.3、1.2.3.4.5全部过一遍才提交。4.3 第三题合并两个有序链表第三题是经典的合并两个有序链表。要求输入两个升序链表的头节点返回合并后链表的头节点。题目难度不高但要求用递归或迭代实现并分析时间复杂度和空间复杂度。迭代实现的模板def merge_two_lists(l1, l2): dummy ListNode(0) cur dummy while l1 and l2: if l1.val l2.val: cur.next l1 l1 l1.next else: cur.next l2 l2 l2.next cur cur.next cur.next l1 if l1 else l2 return dummy.next时间复杂度O(mn)空间复杂度O(1)迭代法。递归法实现更简洁但空间复杂度是O(mn)递归调用栈。笔试时一定要写清楚边界情况两个链表都为空返回空其中一个为空返回另一个。这道题考察的是链表基础的扎实程度。安全公司写脚本处理数据时经常需要操作链表结构所以这类题对安全岗来说并不过分。如果时间充裕可以把递归和迭代两种写法都练熟因为面试官可能让你换一种方式实现。5. 实战避坑与备战建议5.1 时间分配策略这套题整体难度不大但题量不小尤其是编程题需要上机调试比较费时间。我当时的策略是选择题控制在20分钟内简答题控制在40分钟内剩下60分钟左右全部留给编程题。选择题遇到拿不准的先标记跳过不要死磕因为后面分析题的分数权重更高编程题更是决定了你能不能进面试。编程题如果卡住了先把暴力解法写出来至少能过部分测试用例拿一半分。很多同学一上来就想最优解结果写了一个小时没跑通还不如先用简单解法拿基础分再逐步优化。5.2 阅卷踩点与答题格式安全分析题和代码审计题是人工阅卷踩点得分。答题时要注意两点一是原理部分要展开不能只写“存在SQL注入”就完事要把漏洞形成的原因、利用方式、修复方案分开写二是修复代码要写完整最好附上关键代码片段。比如代码审计题你光说“用预处理语句修复”可能只拿一半分贴上完整的bind_param代码才能拿满。另外代码格式要规范缩进要清晰。阅卷人一天看几百份卷子一份排版混乱的答案很容易被扣印象分。写代码时尽量用清晰的变量命名别用a、b、c这种无意义的名字。5.3 后续扩展方向笔试只是第一关通过之后还有面试。面试大概率会围绕笔试内容展开比如让你解释笔试中某道题的思路或者现场写一道变体题。所以笔试结束后不要急着把题目忘掉要趁热打铁把每道题的解法研究透。我当时把日志分析题重新做了一遍把每种攻击类型的日志特征整理成笔记面试时正好被问到直接拿出来讲效果很好。这套题对我后来的帮助很大倒不是说题目本身多有用而是它让我意识到安全岗位的笔试重点不是奇技淫巧而是基础知识的熟练度。TCP握手状态、SQL注入原理、Linux调试命令、代码审计思路、基础算法题这些看起来平平无奇的东西组合起来就是安全工程师日常工作的全部场景。后面不管是投其他安全公司还是做安全开发这些底子都用得上。最后分享一个小技巧备考安全公司笔试时别只刷算法题。每天抽半小时读一段真实漏洞代码比如去GitHub上看一些历史漏洞的修复commit看开发者是怎么改代码的。看得多了你会发现代码审计题的套路就那么几种掌握了规律之后笔试通过率会高很多。
返回列表